├── .babelrc ├── .browserslistrc ├── .editorconfig ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .npmignore ├── .prettierrc ├── LICENSE ├── README.md ├── build ├── build.js ├── copy.js ├── error.js ├── notify.js ├── scripts.js ├── serve.js ├── styles.js ├── svgs.js └── watch.js ├── dist ├── locomotive-scroll.css ├── locomotive-scroll.esm.js ├── locomotive-scroll.js ├── locomotive-scroll.min.css └── locomotive-scroll.min.js ├── docs ├── browserconfig.xml ├── dist │ ├── fonts │ │ ├── NeueMontreal-Medium.woff │ │ ├── NeueMontreal-Medium.woff2 │ │ └── Personal-license-agreement │ ├── images │ │ ├── favicons │ │ │ ├── android-chrome-144x144.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── mstile-150x150.png │ │ │ └── safari-pinned-tab.svg │ │ ├── locomotive01.jpg │ │ ├── locomotive02.jpg │ │ ├── locomotive03.jpg │ │ ├── locomotive04.jpg │ │ └── sprite.svg │ ├── scripts │ │ └── main.js │ └── styles │ │ └── main.css ├── favicon.ico ├── horizontal.html ├── index.html ├── site.webmanifest └── src │ ├── images │ └── sprite │ │ ├── github.svg │ │ └── logo.svg │ ├── scripts │ └── main.js │ └── styles │ ├── components │ ├── _choochoo.scss │ ├── _cta.scss │ ├── _damn.scss │ ├── _direction-block.scss │ ├── _features.scss │ ├── _fixed.scss │ ├── _header.scss │ ├── _helicopter.scss │ ├── _intro.scss │ ├── _lerp-block.scss │ ├── _section.scss │ ├── _speed-block.scss │ └── _summary.scss │ ├── elements │ ├── _fonts.scss │ ├── _headings.scss │ └── _page.scss │ ├── generic │ ├── _button.scss │ ├── _form.scss │ ├── _generic.scss │ └── _media.scss │ ├── main.scss │ ├── objects │ ├── _button.scss │ ├── _container.scss │ ├── _crop.scss │ ├── _form.scss │ ├── _image.scss │ ├── _layout.scss │ ├── _ratio.scss │ ├── _scroll.scss │ ├── _table.scss │ └── _title.scss │ ├── settings │ ├── _config.colors.scss │ └── _config.scss │ ├── templates │ └── .gitkeep │ ├── tools │ ├── _family.scss │ ├── _fonts.scss │ ├── _functions.scss │ ├── _layout.scss │ ├── _mixins.scss │ └── _widths.scss │ ├── utilities │ ├── _align.scss │ ├── _headings.scss │ ├── _helpers.scss │ ├── _print.scss │ ├── _ratio.scss │ ├── _spacing.scss │ ├── _states.scss │ └── _widths.scss │ └── vendors │ └── .gitkeep ├── gulpfile.babel.js ├── mconfig.json ├── package-lock.json ├── package.json ├── src ├── locomotive-scroll.js ├── locomotive-scroll.scss ├── locomotive-scroll.umd.js ├── scripts │ ├── Core.js │ ├── Main.js │ ├── Native.js │ ├── NativeMain.js │ ├── Smooth.js │ ├── options.js │ └── utils │ │ ├── html.js │ │ ├── maths.js │ │ └── transform.js └── styles │ ├── _base.scss │ └── _scrollbar.scss └── www /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 4 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.{md,markdown}] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Hello 👋 11 | 12 | **Describe the bug** 13 | A clear and concise description of what the bug is. 14 | 15 | **To Reproduce** 16 | Steps to reproduce the behavior: 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. See error 21 | 22 | **Expected behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | **Screenshots** 26 | If applicable, add screenshots to help explain your problem. 27 | 28 | **Desktop (please complete the following information):** 29 | - OS: [e.g. iOS] 30 | - Browser [e.g. chrome, safari] 31 | - Version [e.g. 22] 32 | 33 | **Smartphone (please complete the following information):** 34 | - Device: [e.g. iPhone6] 35 | - OS: [e.g. iOS8.1] 36 | - Browser [e.g. stock browser, safari] 37 | - Version [e.g. 22] 38 | 39 | Thank you 👊 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | build 2 | docs 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "none", 4 | "printWidth": 100 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Locomotive, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://img.shields.io/npm/v/locomotive-scroll)](https://www.npmjs.com/package/locomotive-scroll) 2 | [![](https://img.shields.io/npm/dm/locomotive-scroll)](https://www.npmjs.com/package/locomotive-scroll) 3 | 4 | > 🚀 **Locomotive Scroll v5 Beta Release** 5 | > 6 | > Try out the beta version of Locomotive Scroll v5! 7 | > 8 | > 🔗 [Click here to try Locomotive Scroll v5 Beta](https://github.com/locomotivemtl/locomotive-scroll/tree/v5-beta) 9 | > 10 | > Your feedback is valuable during this beta testing phase. If you encounter any issues or have suggestions, please [open an issue](https://github.com/locomotivemtl/locomotive-scroll/issues/new?labels=v5&template=bug_report.md). 11 | > 12 | > Happy scrolling! 😄 13 | 14 | 15 |

16 | 17 | 18 | 19 |

20 |

Locomotive Scroll

21 |

Detection of elements in viewport & smooth scrolling with parallax effects.

22 | 23 | ## Installation 24 | 25 | > ⚠️ Scroll-hijacking is a controversial practice that can cause usability, accessibility, and performance issues. Please use responsibly. 26 | 27 | ```sh 28 | npm install locomotive-scroll 29 | ``` 30 | 31 | ## Usage 32 | 33 | ### Basic 34 | With simple detection. 35 | 36 | #### HTML 37 | ```html 38 |

Hey

39 |

👋

40 | ``` 41 | 42 | #### CSS 43 | Add the base styles to your CSS file. 44 | 45 | [`locomotive-scroll.css`](https://github.com/locomotivemtl/locomotive-scroll/blob/master/dist/locomotive-scroll.css) 46 | 47 | #### JS 48 | 49 | ##### With a bundler 50 | ```js 51 | import LocomotiveScroll from 'locomotive-scroll'; 52 | 53 | const scroll = new LocomotiveScroll(); 54 | ``` 55 | 56 | ##### Or without 57 | ```js 58 | 59 | 64 | ``` 65 | _Get the [JS file](https://github.com/locomotivemtl/locomotive-scroll/blob/master/dist/locomotive-scroll.min.js)._ 66 | 67 | ### Smooth 68 | With smooth scrolling and parallax. 69 | 70 | ```html 71 |
72 |
73 |

Hey

74 |

👋

75 |
76 |
77 |

What's up?

78 |

😬

79 |
80 |
81 | ``` 82 | 83 | ```js 84 | import LocomotiveScroll from 'locomotive-scroll'; 85 | 86 | const scroll = new LocomotiveScroll({ 87 | el: document.querySelector('[data-scroll-container]'), 88 | smooth: true 89 | }); 90 | ``` 91 | 92 | _Note: scroll-sections are optional but recommended to improve performance, particularly in long pages._ 93 | 94 | ### Advanced 95 | Make it do what you want. 96 | 97 | #### With methods 98 | ```html 99 |
Come here please.
100 | ``` 101 | 102 | ```js 103 | import LocomotiveScroll from 'locomotive-scroll'; 104 | 105 | const scroll = new LocomotiveScroll(); 106 | const target = document.querySelector('#js-target'); 107 | 108 | scroll.scrollTo(target); 109 | ``` 110 | 111 | #### With events 112 | 113 | ```html 114 | 115 |
Trigger
116 | 117 |
Trigger
118 | 119 |
Trigger
120 | ``` 121 | 122 | ```js 123 | import LocomotiveScroll from 'locomotive-scroll'; 124 | 125 | const scroll = new LocomotiveScroll(); 126 | 127 | scroll.on('call', func => { 128 | // Using modularJS 129 | this.call(...func); 130 | // Using jQuery events 131 | $(document).trigger(func); 132 | // Or do it your own way 😎 133 | }); 134 | ``` 135 | 136 | ## Instance options 137 | 138 | | Option | Type | Default | Description | 139 | | ----------------------- | --------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 140 | | `el` | `object` | `document` | Scroll container element. | 141 | | `name` | `string` | `'scroll'` | Data attribute prefix (`data-scroll-xxxx`). | | 142 | | `offset` | `array(2)`| `[0,0]` | Global in-view trigger offset : `[bottom,top]`
Use a string with `%` to use a percentage of the viewport height.
Use a numeric value for absolute pixels unit.
E.g. `["30%",0]`, `[100,0]`, `["30%", 100]` | 143 | | `repeat` | `boolean` | `false` | Repeat in-view detection. | 144 | | `smooth` | `boolean` | `false` | Smooth scrolling. | 145 | | `initPosition` | `object` | `{ x: 0, y: 0 }` | ![Smooth only][smooth-only]
An `object` defining the initial scroll coordinates on a smooth instance. For example: `{ x: 0, y: 1000 }` | 146 | | `direction` | `string` | `vertical` | ![Smooth only][smooth-only]
Scroll direction: `vertical` or `horizontal` | 147 | | `lerp` | `number` | `0.1` | ![Smooth only][smooth-only]
Linear interpolation (lerp) intensity. Float between `0` and `1`.
This defines the "smoothness" intensity. The closer to `0`, the smoother. | 148 | | `getDirection` | `boolean` | `false` | Add direction to scroll event. | 149 | | `getSpeed` | `boolean` | `false` | Add speed to scroll event. | 150 | | `class` | `string` | `is-inview` | Element in-view class. | 151 | | `initClass` | `string` | `has-scroll-init` | Initialize class. | 152 | | `scrollingClass` | `string` | `has-scroll-scrolling` | Is scrolling class. | 153 | | `draggingClass` | `string` | `has-scroll-dragging` | Is dragging class. | 154 | | `smoothClass` | `string` | `has-scroll-smooth` | Has smooth scrolling class. | 155 | | `scrollbarContainer` | `object` | `false` | ![Smooth only][smooth-only]
Specifies the container element for the scrollbar to be appended in. If false, scrollbar will be appended to the body. | 156 | | `scrollbarClass` | `string` | `c-scrollbar` | ![Smooth only][smooth-only]
Scrollbar element class. | 157 | | `multiplier` | `number` | `1` | ![Smooth only][smooth-only]
Factor applied to the scroll delta, allowing to boost/reduce scrolling speed (regardless of the platform). | 158 | | `firefoxMultiplier` | `number` | `50` | ![Smooth only][smooth-only]
Boost scrolling speed of Firefox on Windows. | 159 | | `touchMultiplier` | `number` | `2` | ![Smooth only][smooth-only]
Multiply touch action to scroll faster than finger movement. | 160 | | `scrollFromAnywhere` | `boolean` | `false` | ![Smooth only][smooth-only]
By default locomotive-scroll listens for scroll events only on the scroll container (`el` option). With this option set to true, it listens on the whole document instead. | 161 | | `gestureDirection` | `string` | `vertical` | ![Smooth only][smooth-only]
Defines which gesture direction(s) scrolls in your instance. You can use : | 162 | | `tablet` & `smartphone` | `object` | | Object allowing to override some options for a particular context. You can specify: For `tablet` context you can also define `breakpoint` (_integer_, defaults to 1024) to set the max-width breakpoint for tablets. | 163 | | `reloadOnContextChange` | `boolean` | `false` | Allows to reload the page when switching between `desktop`, `tablet` and `smartphone` contexts. It can be useful if your page changes a lot between contexts and you want to reset everything. | 164 | | `resetNativeScroll` | `boolean` | `true` | Sets `history.scrollRestoration = 'manual'` and calls `window.scrollTo(0, 0)` on locomotive-scroll init in Native Class. Useful if you use transitions with native scrolling, otherwise we advise to set it to `false` if you don't want to break History API's scroll restoration feature. | 165 | 166 | ## Element attributes 167 | 168 | | Attribute | Values | Description | 169 | | ----------------------- | ------------------------ | ---------------------------------------------------------------------------------------- | 170 | | `data-scroll` | | Detect if in-view. | 171 | | `data-scroll-id` | `string` | (Optional) Useful if you want to scope your element and get the progress of your element in the viewport for example. | 172 | | `data-scroll-container` | | Defines the scroll container. Required for [basic styling](https://github.com/locomotivemtl/locomotive-scroll/blob/master/dist/locomotive-scroll.css). | 173 | | `data-scroll-section` | | Defines a scrollable section. Splitting your page into sections may improve performance. | 174 | | `data-scroll-class` | `string` | Element in-view class. | 175 | | `data-scroll-offset` | `string` | Element in-view trigger offset : `bottom,top`
First value is `bottom` offset, second (optional) is `top` offset.
Percent is relative to viewport height, otherwise it's absolute pixels.
E.g. `"10"`, `"100,50%"`, `"25%, 15%"` | 176 | | `data-scroll-repeat` | `boolean` | Element in-view detection repeat. | 177 | | `data-scroll-call` | `string` | Element in-view trigger call event. | 178 | | `data-scroll-position` | `string` | `top`, `bottom`, `left` or `right`
Window position of in-view trigger. | 179 | | `data-scroll-speed` | `number` | ![Smooth only][smooth-only]
Element parallax speed. A negative value will reverse the direction. | 180 | | `data-scroll-delay` | `number` | ![Smooth only][smooth-only]
Element's parallax lerp delay. | 181 | | `data-scroll-direction` | `string` | ![Smooth only][smooth-only]
Element's parallax direction. `vertical` or `horizontal` | 182 | | `data-scroll-sticky` | | ![Smooth only][smooth-only]
Sticky element. Starts and stops at `data-scroll-target` position. | 183 | | `data-scroll-target` | `string` | ![Smooth only][smooth-only]
Target element's in-view position. | 184 | 185 | ## Instance methods 186 | 187 | | Method | Description | Arguments | 188 | | -------------------------- | ------------------------------ | ------------------------------------------------------------------------------- | 189 | | `init()` | Reinitializes the scroll. | | 190 | | `on(eventName, function)` | Listen [instance events] ⬇. | | 191 | | `update()` | Updates all element positions. | | 192 | | `destroy()` | Destroys the scroll events. | | 193 | | `start()` | Restarts the scroll events. | | 194 | | `stop()` | Stops the scroll events. | | 195 | | `scrollTo(target, options)`| Scroll to a target. |
`target`: Defines where you want to scroll. Available values types are :
`options` (optional, _object_) : Settings object. Available values are: | 196 | 197 | ## Instance events 198 | 199 | | Event | Arguments | Description | 200 | | -------- | --------- | --------------------------------------------------------------------- | 201 | | `scroll` | `obj` | Returns scroll instance (position, limit, speed, direction and current in-view elements). | 202 | | `call` | `func` | Trigger if in-view. Returns your `string` or `array` if contains `,`. | 203 | 204 | ## Progressive playing animations example (like gsap) 205 | All `data-scroll` elements have a progress value. 206 | In the on scroll event you can get all current in-view elements. 207 | #### HTML 208 | ```html 209 |

Hey

210 | ``` 211 | #### JS 212 | ```js 213 | scroll.on('scroll', (args) => { 214 | // Get all current elements : args.currentElements 215 | if(typeof args.currentElements['hey'] === 'object') { 216 | let progress = args.currentElements['hey'].progress; 217 | console.log(progress); 218 | // ouput log example: 0.34 219 | // gsap example : myGsapAnimation.progress(progress); 220 | } 221 | }); 222 | ``` 223 | 224 | 225 | ## Dependencies 226 | 227 | | Name | Description | 228 | | ---------------- | ------------------------------------------------------------------ | 229 | | [Virtual Scroll] | Custom scroll event with inertia/momentum. | 230 | | [modularScroll] | Elements in viewport detection. Forked from it, not a dependency. | 231 | | [bezier-easing] | Allows to define an easing to `scrollTo` movement | 232 | 233 | [instance events]: #instance-events 234 | [Virtual Scroll]: https://github.com/ayamflow/virtual-scroll 235 | [modularScroll]: https://github.com/modularorg/modularscroll 236 | [bezier-easing]: https://github.com/gre/bezier-easing 237 | 238 | 239 | ## Browser support 240 | 241 | Works on most modern browsers. Chrome, Firefox, Safari, Edge... 242 | 243 | To get IE 11 support, you need polyfills. 244 | You can use your own or include these before our script. 245 | 246 | ```html 247 | 248 | 249 | ``` 250 | 251 | ## Who's using Locomotive Scroll? 252 | - [thierrychopain.com](https://thierrychopain.com/) 253 | - [clmt.paris](https://clmt.paris/) 254 | - [miragefestival.com/2020](https://www.miragefestival.com/2020/) 255 | - [mazellier.design](https://www.mazellier.design/) 256 | - [ccccontemple.com](https://ccccontemple.com/) 257 | - [abhishekjha.me/muteza](https://abhishekjha.me/muteza/) 258 | - [normal.studio](https://normal.studio/en/) 259 | - [mixlegno.com](https://www.mixlegno.com/) 260 | - [nfq.group](https://nfq.group/) 261 | - [works.studio](https://works.studio/) 262 | - [beangels.eu](https://www.beangels.eu/) 263 | - [izakaya-caen.fr](https://www.izakaya-caen.fr/) 264 | - [white-elephant.fr](https://www.white-elephant.fr/) 265 | - [henge07.com](https://www.henge07.com/) 266 | - [loirevalleylodges.com](https://loirevalleylodges.com/) 267 | 268 | ## Related 269 | 270 | - [Locomotive Boilerplate 🚂](https://github.com/locomotivemtl/locomotive-boilerplate) 271 | 272 | [smooth-only]: https://img.shields.io/badge/smooth-only-blue 273 | -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import postcss from 'gulp-postcss'; 3 | import rename from 'gulp-rename'; 4 | import cssnano from 'cssnano'; 5 | import merge from 'merge-stream'; 6 | import terser from 'gulp-terser-js'; 7 | import htmlmin from 'gulp-htmlmin'; 8 | import paths from '../mconfig.json'; 9 | 10 | function buildStyles() { 11 | const stylesfiles = [ 12 | { 13 | dest: paths.styles.dest 14 | }, 15 | { 16 | dest: paths.styles.docs.dest 17 | } 18 | ]; 19 | 20 | const plugins = [ 21 | cssnano() 22 | ]; 23 | 24 | const stylesStreams = stylesfiles.map((file) => { 25 | return gulp 26 | .src([file.dest+'*.css', '!'+file.dest+'*.min.css']) 27 | .pipe(rename(function(file) { 28 | if (file.basename == paths.styles.main) { 29 | file.basename += '.min'; 30 | } 31 | })) 32 | .pipe(postcss(plugins)) 33 | .pipe(gulp.dest(file.dest)); 34 | }); 35 | 36 | return merge(stylesStreams); 37 | } 38 | 39 | function buildScripts() { 40 | const scriptsfiles = [ 41 | { 42 | dest: paths.scripts.dest 43 | }, 44 | { 45 | dest: paths.scripts.docs.dest 46 | } 47 | ]; 48 | 49 | const scriptsStreams = scriptsfiles.map((file) => { 50 | return gulp 51 | .src([file.dest+'*.js', '!'+file.dest+'*.esm.js', '!'+file.dest+'*.min.js']) 52 | .pipe(rename(function(file) { 53 | if (file.basename == paths.scripts.main) { 54 | file.basename += '.min'; 55 | } 56 | })) 57 | .pipe(terser({ 58 | output: { 59 | comments: false 60 | } 61 | })) 62 | .pipe(gulp.dest(file.dest)); 63 | }); 64 | 65 | return merge(scriptsStreams); 66 | } 67 | 68 | function buildViews() { 69 | return gulp 70 | .src(paths.dest + '*.html') 71 | .pipe(htmlmin('collapseWhitespace: true')) 72 | .pipe(gulp.dest(paths.dest)); 73 | } 74 | 75 | export { buildStyles, buildScripts, buildViews }; 76 | -------------------------------------------------------------------------------- /build/copy.js: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import paths from '../mconfig.json'; 3 | import error from './error.js'; 4 | 5 | function copy() { 6 | return gulp 7 | .src([`./node_modules/locomotive-scroll/assets/scripts/scroll/vendors/*`]) 8 | .on('error', function(err) { 9 | error(this, err); 10 | }) 11 | .pipe(gulp.dest(`${paths.scripts.src}/scroll/vendors`)); 12 | } 13 | 14 | export default copy; 15 | -------------------------------------------------------------------------------- /build/error.js: -------------------------------------------------------------------------------- 1 | import notify from './notify.js'; 2 | 3 | function error(object, error, type) { 4 | const message = (type == 'stack') ? error.stack : error.toString(); 5 | console.error(message); 6 | notify('Error', error.message); 7 | object.emit('end'); 8 | } 9 | 10 | export default error; 11 | -------------------------------------------------------------------------------- /build/notify.js: -------------------------------------------------------------------------------- 1 | import notifier from 'node-notifier'; 2 | 3 | function notify(title, message) { 4 | notifier.notify({ 5 | title: title, 6 | message: message 7 | }); 8 | } 9 | 10 | export default notify; 11 | -------------------------------------------------------------------------------- /build/scripts.js: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import { rollup } from 'rollup'; 3 | import resolve from 'rollup-plugin-node-resolve'; 4 | import babel from 'rollup-plugin-babel'; 5 | import common from 'rollup-plugin-commonjs'; 6 | import paths from '../mconfig.json'; 7 | import pkg from '../package.json'; 8 | 9 | function scripts() { 10 | const files = [ 11 | { 12 | src: paths.scripts.src + paths.scripts.main, 13 | dest: paths.scripts.dest + paths.scripts.main + '.esm', 14 | format: 'esm' 15 | }, 16 | { 17 | src: paths.scripts.src + paths.scripts.main + '.umd', 18 | dest: paths.scripts.dest + paths.scripts.main, 19 | format: 'umd' 20 | }, 21 | { 22 | src: paths.scripts.docs.src + paths.scripts.docs.main, 23 | dest: paths.scripts.docs.dest + paths.scripts.docs.main, 24 | format: 'iife' 25 | } 26 | ]; 27 | 28 | const promises = files.map((file) => { 29 | return rollup({ 30 | input: file.src + '.js', 31 | plugins: [ 32 | resolve(), 33 | babel({ 34 | exclude: 'node_modules/**' 35 | }), 36 | common({ 37 | include: 'node_modules/**' 38 | }), 39 | ] 40 | }).then(bundle => { 41 | return bundle.write({ 42 | file: file.dest + '.js', 43 | name: 'LocomotiveScroll', 44 | format: file.format, 45 | banner: '/* locomotive-scroll v' + pkg.version + ' | MIT License | https://github.com/locomotivemtl/locomotive-scroll */' 46 | }); 47 | }) 48 | }); 49 | 50 | return Promise.all(promises); 51 | } 52 | 53 | export default scripts; 54 | -------------------------------------------------------------------------------- /build/serve.js: -------------------------------------------------------------------------------- 1 | import browserSync from 'browser-sync'; 2 | import paths from '../mconfig.json'; 3 | 4 | export const server = browserSync.create(); 5 | 6 | function serve(done) { 7 | server.init({ 8 | notify: false, 9 | server: { 10 | baseDir: paths.docs.dest, 11 | serveStaticOptions: { 12 | extensions: ['html'] 13 | } 14 | }, 15 | open: false, 16 | ghostMode: false 17 | }); 18 | done(); 19 | } 20 | 21 | export default serve; 22 | -------------------------------------------------------------------------------- /build/styles.js: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import gulpSass from 'gulp-sass'; 3 | import autoprefixer from 'gulp-autoprefixer'; 4 | import header from 'gulp-header'; 5 | import merge from 'merge-stream'; 6 | import nodeSass from 'node-sass'; 7 | import paths from '../mconfig.json'; 8 | import pkg from '../package.json'; 9 | import error from './error.js'; 10 | import { server } from './serve.js'; 11 | 12 | const sass = gulpSass(nodeSass); 13 | 14 | function styles() { 15 | const files = [ 16 | { 17 | src: paths.styles.src, 18 | dest: paths.styles.dest 19 | }, 20 | { 21 | src: paths.styles.docs.src, 22 | dest: paths.styles.docs.dest 23 | } 24 | ]; 25 | 26 | const streams = files.map((file) => { 27 | return gulp 28 | .src(file.src + '**/*.scss') 29 | .pipe(sass().on('error', function (err) { 30 | error(this, err, 'stack'); 31 | })) 32 | .pipe(autoprefixer({ 33 | cascade: false 34 | })) 35 | .pipe(header('/*! locomotive-scroll v' + pkg.version + ' | MIT License | https://github.com/locomotivemtl/locomotive-scroll */\n')) 36 | .pipe(gulp.dest(file.dest)) 37 | .pipe(server.stream()); 38 | }); 39 | 40 | return merge(streams); 41 | } 42 | 43 | export default styles; 44 | -------------------------------------------------------------------------------- /build/svgs.js: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import path from 'path'; 3 | import rename from 'gulp-rename'; 4 | import svgstore from 'gulp-svgstore'; 5 | import paths from '../mconfig.json'; 6 | import error from './error.js'; 7 | 8 | function svgs() { 9 | return gulp 10 | .src(paths.svgs.src + '**/*.svg', { base: paths.svgs.src }) 11 | .pipe(rename(function(file) { 12 | if (file.dirname != '.') { 13 | const name = file.dirname.split(path.sep); 14 | name.push(file.basename); 15 | file.basename = name.join('-'); 16 | } 17 | })) 18 | .pipe(svgstore()) 19 | .on('error', function(err) { 20 | error(this, err); 21 | }) 22 | .pipe(gulp.dest(paths.svgs.dest)); 23 | } 24 | 25 | export default svgs; 26 | -------------------------------------------------------------------------------- /build/watch.js: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import paths from '../mconfig.json'; 3 | import styles from './styles.js'; 4 | import scripts from './scripts.js'; 5 | import svgs from './svgs.js'; 6 | import { server } from './serve.js'; 7 | 8 | function watch() { 9 | gulp.watch([paths.styles.src, paths.styles.docs.src], styles); 10 | gulp.watch([paths.scripts.src, paths.scripts.docs.src], gulp.series(scripts, reload)); 11 | gulp.watch(paths.views.src, reload); 12 | gulp.watch(paths.svgs.src, gulp.series(svgs, reload)); 13 | } 14 | 15 | function reload(done) { 16 | server.reload(); 17 | done(); 18 | } 19 | 20 | export default watch; 21 | -------------------------------------------------------------------------------- /dist/locomotive-scroll.css: -------------------------------------------------------------------------------- 1 | /*! locomotive-scroll v4.1.3 | MIT License | https://github.com/locomotivemtl/locomotive-scroll */ 2 | html.has-scroll-smooth { 3 | overflow: hidden; } 4 | 5 | html.has-scroll-dragging { 6 | -webkit-user-select: none; 7 | -moz-user-select: none; 8 | -ms-user-select: none; 9 | user-select: none; } 10 | 11 | .has-scroll-smooth body { 12 | overflow: hidden; } 13 | 14 | .has-scroll-smooth [data-scroll-container] { 15 | min-height: 100vh; } 16 | 17 | [data-scroll-direction="horizontal"] [data-scroll-container] { 18 | height: 100vh; 19 | display: inline-block; 20 | white-space: nowrap; } 21 | 22 | [data-scroll-direction="horizontal"] [data-scroll-section] { 23 | display: inline-block; 24 | vertical-align: top; 25 | white-space: nowrap; 26 | height: 100%; } 27 | 28 | .c-scrollbar { 29 | position: absolute; 30 | right: 0; 31 | top: 0; 32 | width: 11px; 33 | height: 100%; 34 | transform-origin: center right; 35 | transition: transform 0.3s, opacity 0.3s; 36 | opacity: 0; } 37 | .c-scrollbar:hover { 38 | transform: scaleX(1.45); } 39 | .c-scrollbar:hover, .has-scroll-scrolling .c-scrollbar, .has-scroll-dragging .c-scrollbar { 40 | opacity: 1; } 41 | [data-scroll-direction="horizontal"] .c-scrollbar { 42 | width: 100%; 43 | height: 10px; 44 | top: auto; 45 | bottom: 0; 46 | transform: scaleY(1); } 47 | [data-scroll-direction="horizontal"] .c-scrollbar:hover { 48 | transform: scaleY(1.3); } 49 | 50 | .c-scrollbar_thumb { 51 | position: absolute; 52 | top: 0; 53 | right: 0; 54 | background-color: black; 55 | opacity: 0.5; 56 | width: 7px; 57 | border-radius: 10px; 58 | margin: 2px; 59 | cursor: -webkit-grab; 60 | cursor: grab; } 61 | .has-scroll-dragging .c-scrollbar_thumb { 62 | cursor: -webkit-grabbing; 63 | cursor: grabbing; } 64 | [data-scroll-direction="horizontal"] .c-scrollbar_thumb { 65 | right: auto; 66 | bottom: 0; } 67 | -------------------------------------------------------------------------------- /dist/locomotive-scroll.min.css: -------------------------------------------------------------------------------- 1 | /*! locomotive-scroll v4.1.3 | MIT License | https://github.com/locomotivemtl/locomotive-scroll */html.has-scroll-smooth{overflow:hidden}html.has-scroll-dragging{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.has-scroll-smooth body{overflow:hidden}.has-scroll-smooth [data-scroll-container]{min-height:100vh}[data-scroll-direction=horizontal] [data-scroll-container]{display:inline-block;height:100vh;white-space:nowrap}[data-scroll-direction=horizontal] [data-scroll-section]{display:inline-block;height:100%;vertical-align:top;white-space:nowrap}.c-scrollbar{height:100%;opacity:0;position:absolute;right:0;top:0;transform-origin:center right;transition:transform .3s,opacity .3s;width:11px}.c-scrollbar:hover{transform:scaleX(1.45)}.c-scrollbar:hover,.has-scroll-dragging .c-scrollbar,.has-scroll-scrolling .c-scrollbar{opacity:1}[data-scroll-direction=horizontal] .c-scrollbar{bottom:0;height:10px;top:auto;transform:scaleY(1);width:100%}[data-scroll-direction=horizontal] .c-scrollbar:hover{transform:scaleY(1.3)}.c-scrollbar_thumb{background-color:#000;border-radius:10px;cursor:-webkit-grab;cursor:grab;margin:2px;opacity:.5;position:absolute;right:0;top:0;width:7px}.has-scroll-dragging .c-scrollbar_thumb{cursor:-webkit-grabbing;cursor:grabbing}[data-scroll-direction=horizontal] .c-scrollbar_thumb{bottom:0;right:auto} -------------------------------------------------------------------------------- /docs/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #ffffff 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/dist/fonts/NeueMontreal-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/locomotivemtl/locomotive-scroll/8ac3990434182eb3e782edeae3d16e1cdf3ac015/docs/dist/fonts/NeueMontreal-Medium.woff -------------------------------------------------------------------------------- /docs/dist/fonts/NeueMontreal-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/locomotivemtl/locomotive-scroll/8ac3990434182eb3e782edeae3d16e1cdf3ac015/docs/dist/fonts/NeueMontreal-Medium.woff2 -------------------------------------------------------------------------------- /docs/dist/fonts/Personal-license-agreement: -------------------------------------------------------------------------------- 1 | 2 | Personal License Agreement 3 | 4 | Pangram Pangram® 5 | pangrampangram.com 6 | 7 | These freely downloaded weights are for personal use only. 8 | Do not resell, redistribute or alter in any way without the consent of Pangram Pangram®. 9 | 10 | By using these freely downloaded fonts, you agree to these terms. 11 | 12 | For any commercial licenses, please go to https://pangrampangram.com 13 | 14 | Thank you and enjoy! 15 | 16 | Mat at Pangram Pangram® 17 | 18 | -------------------------------------------------------------------------------- /docs/dist/images/favicons/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/locomotivemtl/locomotive-scroll/8ac3990434182eb3e782edeae3d16e1cdf3ac015/docs/dist/images/favicons/android-chrome-144x144.png -------------------------------------------------------------------------------- /docs/dist/images/favicons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/locomotivemtl/locomotive-scroll/8ac3990434182eb3e782edeae3d16e1cdf3ac015/docs/dist/images/favicons/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/dist/images/favicons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/locomotivemtl/locomotive-scroll/8ac3990434182eb3e782edeae3d16e1cdf3ac015/docs/dist/images/favicons/favicon-16x16.png -------------------------------------------------------------------------------- /docs/dist/images/favicons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/locomotivemtl/locomotive-scroll/8ac3990434182eb3e782edeae3d16e1cdf3ac015/docs/dist/images/favicons/favicon-32x32.png -------------------------------------------------------------------------------- /docs/dist/images/favicons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/locomotivemtl/locomotive-scroll/8ac3990434182eb3e782edeae3d16e1cdf3ac015/docs/dist/images/favicons/mstile-150x150.png -------------------------------------------------------------------------------- /docs/dist/images/favicons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 58 | 98 | 100 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /docs/dist/images/locomotive01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/locomotivemtl/locomotive-scroll/8ac3990434182eb3e782edeae3d16e1cdf3ac015/docs/dist/images/locomotive01.jpg -------------------------------------------------------------------------------- /docs/dist/images/locomotive02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/locomotivemtl/locomotive-scroll/8ac3990434182eb3e782edeae3d16e1cdf3ac015/docs/dist/images/locomotive02.jpg -------------------------------------------------------------------------------- /docs/dist/images/locomotive03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/locomotivemtl/locomotive-scroll/8ac3990434182eb3e782edeae3d16e1cdf3ac015/docs/dist/images/locomotive03.jpg -------------------------------------------------------------------------------- /docs/dist/images/locomotive04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/locomotivemtl/locomotive-scroll/8ac3990434182eb3e782edeae3d16e1cdf3ac015/docs/dist/images/locomotive04.jpg -------------------------------------------------------------------------------- /docs/dist/images/sprite.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | github copy 6 | Created with Sketch. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/locomotivemtl/locomotive-scroll/8ac3990434182eb3e782edeae3d16e1cdf3ac015/docs/favicon.ico -------------------------------------------------------------------------------- /docs/horizontal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Locomotive Scroll | Detection of elements in viewport & smooth scrolling with parallax effects. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 26 | 27 | 28 |
29 | 30 |
31 |

32 |

33 | Welcome to 34 |

35 |

36 | Horizontal 37 |

38 |

39 | Scrolling 40 |

41 |

42 |
43 | 44 |
45 |
46 |
47 | 🚁 48 |
49 |
50 |
51 | 52 |
53 |
54 |
55 | Random image 56 |
57 |
58 |
59 |
60 |
61 |
62 | Random image 63 |
64 |
65 |
66 |
67 |
68 |
69 | Random image 70 |
71 |
72 |
73 | 74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /docs/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Locomotive Scroll", 3 | "short_name": "Locomotive Scroll", 4 | "icons": [ 5 | { 6 | "src": "dist/images/favicons/android-chrome-144x144.png", 7 | "sizes": "144x144", 8 | "type": "image/png" 9 | } 10 | ], 11 | "theme_color": "#ffffff", 12 | "background_color": "#ffffff", 13 | "display": "standalone" 14 | } 15 | -------------------------------------------------------------------------------- /docs/src/images/sprite/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | github copy 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/src/images/sprite/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | locomotive 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/src/scripts/main.js: -------------------------------------------------------------------------------- 1 | import LocomotiveScroll from '../../../src/locomotive-scroll'; 2 | 3 | (function() { 4 | 5 | document.documentElement.classList.add('is-loaded'); 6 | document.documentElement.classList.remove('is-loading'); 7 | 8 | setTimeout(() => { 9 | document.documentElement.classList.add('is-ready'); 10 | },300) 11 | 12 | let options = { 13 | el: document.querySelector('#js-scroll'), 14 | smooth: true, 15 | getSpeed: true, 16 | getDirection: true 17 | } 18 | 19 | if(document.querySelector('#js-scroll').getAttribute('data-horizontal') == 'true') { 20 | options.direction = 'horizontal'; 21 | options.gestureDirection = 'both'; 22 | options.tablet = { 23 | smooth: true, 24 | direction: 'horizontal', 25 | horizontalGesture: true 26 | } 27 | options.smartphone = { 28 | smooth: false 29 | } 30 | options.reloadOnContextChange = true 31 | } 32 | 33 | setTimeout(() => { 34 | const scroll = new LocomotiveScroll(options); 35 | 36 | let dynamicBackgrounds = []; 37 | let dynamicColorElements = []; 38 | 39 | scroll.on('scroll', (instance) => { 40 | const progress = 360 * instance.scroll.y / instance.limit.y; 41 | 42 | scroll.el.style.backgroundColor = `hsl(${progress}, 11%, 81%)`; 43 | 44 | dynamicBackgrounds.forEach(obj => { 45 | obj.el.style.backgroundColor = `hsl(${progress}, 11%, 81%)`; 46 | }); 47 | dynamicColorElements.forEach(obj => { 48 | obj.el.style.color = `hsl(${progress}, 11%, 81%)`; 49 | }); 50 | 51 | document.documentElement.setAttribute('data-direction', instance.direction) 52 | 53 | }); 54 | 55 | scroll.on('call', (value, way, obj) => { 56 | if (value === 'dynamicBackground') { 57 | if(way === 'enter') { 58 | dynamicBackgrounds.push({ 59 | id: obj.id, 60 | el: obj.el 61 | }); 62 | } else { 63 | for (var i = 0; i < dynamicBackgrounds.length; i++) { 64 | if(obj.id === dynamicBackgrounds[i].id) { 65 | dynamicBackgrounds.splice(i,1); 66 | } 67 | } 68 | } 69 | } else if (value === 'dynamicColor') { 70 | if(way === 'enter') { 71 | dynamicColorElements.push({ 72 | id: obj.id, 73 | el: obj.el 74 | }); 75 | } else { 76 | for (var i = 0; i < dynamicColorElements.length; i++) { 77 | if(obj.id === dynamicColorElements[i].id) { 78 | dynamicColorElements.splice(i,1); 79 | } 80 | } 81 | } 82 | } 83 | }); 84 | 85 | }, 1000) 86 | 87 | })(); 88 | -------------------------------------------------------------------------------- /docs/src/styles/components/_choochoo.scss: -------------------------------------------------------------------------------- 1 | .c-choochoo_item { 2 | padding: 50vh 0; 3 | font-size: rem(100px); 4 | 5 | span { 6 | display: block; 7 | } 8 | 9 | &.-reverse { 10 | span { 11 | transform: rotateY(180deg); 12 | } 13 | } 14 | 15 | html[data-direction="up"] & { 16 | span { 17 | transform: rotateY(180deg); 18 | } 19 | &.-reverse { 20 | span { 21 | transform: rotateY(0deg); 22 | } 23 | } 24 | } 25 | 26 | @media (max-width: $to-large) { 27 | display: none; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /docs/src/styles/components/_cta.scss: -------------------------------------------------------------------------------- 1 | .c-cta_line { 2 | margin: 0; 3 | transform: scaleX(0); 4 | transition: transform 0.6s $easing; 5 | 6 | &.-margin { 7 | margin-bottom: 30px; 8 | } 9 | 10 | &.is-inview { 11 | transform: scaleX(1) 12 | } 13 | } 14 | 15 | .c-cta_content { 16 | margin: rem($unit) 0; 17 | } 18 | 19 | .c-cta_content_text { 20 | max-width: 520px; 21 | 22 | > * { 23 | opacity: 0; 24 | transform: translateY(60px); 25 | transition: opacity 0.6s $easing, transform 0.6s $easing; 26 | 27 | &:not(:first-child) { 28 | margin-top: rem(30px); 29 | } 30 | } 31 | 32 | &.is-inview { 33 | > * { 34 | opacity: 1; 35 | transform: none; 36 | 37 | @for $i from 1 through 3 { 38 | &:nth-child(#{$i}){ 39 | transition-delay: $i*0.06s; 40 | } 41 | } 42 | } 43 | } 44 | } 45 | 46 | .c-cta_button { 47 | opacity: 0; 48 | transform: translateY(60px); 49 | transition: opacity 0.6s $easing, transform 0.6s $easing; 50 | margin-top: rem(90px); 51 | 52 | &.is-inview { 53 | opacity: 1; 54 | transform: none; 55 | } 56 | } 57 | 58 | .c-cta_section { 59 | margin-bottom: rem(180px); 60 | } 61 | -------------------------------------------------------------------------------- /docs/src/styles/components/_damn.scss: -------------------------------------------------------------------------------- 1 | .c-damn { 2 | @media (max-width: $to-medium) { 3 | padding: 150vh 0 0 0; 4 | } 5 | 6 | @media (min-width: $from-medium) { 7 | padding: 100vh 0 0 0; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /docs/src/styles/components/_direction-block.scss: -------------------------------------------------------------------------------- 1 | .c-direction-block_wrapper { 2 | margin-bottom: rem(500px); 3 | 4 | @media (min-width: $from-large) { 5 | min-height: 180vh; 6 | } 7 | @media (min-width: $from-medium) and (max-width: $to-large) { 8 | min-height: 130vh; 9 | } 10 | @media (min-width: $from-small) and (max-width: $to-medium) { 11 | min-height: 100vh; 12 | } 13 | @media (max-width: $to-small) { 14 | min-height: 70vh; 15 | } 16 | 17 | @media (min-width: $from-medium) { 18 | margin-top: -35vh; 19 | } 20 | } 21 | 22 | .c-direction-block { 23 | position: absolute; 24 | right: 0; 25 | left: 0; 26 | z-index: 1; 27 | 28 | @media (min-width: $from-large) { 29 | top: -50vh; 30 | bottom: -50vh; 31 | } 32 | @media (min-width: $from-small) and (max-width: $to-large) { 33 | top: -30vh; 34 | bottom: -50vh; 35 | } 36 | @media (max-width: $to-small) { 37 | top: -15vh; 38 | bottom: 0; 39 | } 40 | 41 | @media (min-width: $from-medium) and (max-width: $to-large) { 42 | margin-top: rem(100px); 43 | } 44 | 45 | @media (min-width: $from-large) { 46 | margin-top: rem(300px); 47 | } 48 | } 49 | 50 | .c-direction-block_item { 51 | position: absolute; 52 | font-size: 10vw; 53 | white-space: nowrap; 54 | font-weight: $semi-bold; 55 | text-transform: uppercase; 56 | line-height: 1; 57 | 58 | span { 59 | display: block; 60 | background-color: $white; 61 | white-space: nowrap; 62 | padding: 0 20px; 63 | } 64 | 65 | &.-one { 66 | top: 33%; 67 | transform: translateX(-50vw) rotate(26deg) 68 | } 69 | &.-two { 70 | top: 45%; 71 | // transform: translateX(-50vw); 72 | } 73 | &.-three { 74 | top: 55%; 75 | transform: rotate(9deg) 76 | } 77 | &.-four { 78 | top: 68%; 79 | transform: translateX(-50vw) rotate(-19deg) 80 | } 81 | &.-five { 82 | top: 63%; 83 | transform: translateX(-10vw) rotate(3deg) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /docs/src/styles/components/_features.scss: -------------------------------------------------------------------------------- 1 | .c-features { 2 | padding: rem($unit * 2) 0; 3 | } 4 | 5 | .c-features_item { 6 | background-color: rgba(0,0,0,0.1); 7 | border-radius: 20px; 8 | padding: rem($unit); 9 | margin-bottom: rem($unit); 10 | } 11 | -------------------------------------------------------------------------------- /docs/src/styles/components/_fixed.scss: -------------------------------------------------------------------------------- 1 | 2 | .c-fixed_wrapper { 3 | position: relative; 4 | overflow: hidden; 5 | background-color: $beige; 6 | 7 | @media (min-width: $from-medium) { 8 | height: 100vh; 9 | } 10 | @media (max-width: $to-medium) { 11 | height: 50vh; 12 | } 13 | } 14 | 15 | .c-fixed_target { 16 | position: absolute; 17 | top: -100vh; 18 | bottom: -100vh; 19 | right: 0; 20 | left: 0; 21 | } 22 | 23 | .c-fixed { 24 | position: absolute; 25 | top: -100vh; 26 | right: 0; 27 | left: 0; 28 | height: 100%; 29 | background-size: cover; 30 | background-position: center center; 31 | opacity: 0.75; 32 | mix-blend-mode: multiply; 33 | 34 | html:not(.has-scroll-smooth) & { 35 | top: 0; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /docs/src/styles/components/_header.scss: -------------------------------------------------------------------------------- 1 | .c-header { 2 | position: relative; 3 | 4 | @media (max-width: $to-small) { 5 | height: 50vh; 6 | min-height: 340px; 7 | } 8 | 9 | @media (min-width: $from-small) and (max-width: $to-medium) { 10 | height: 80vh; 11 | min-height: 380px; 12 | } 13 | 14 | @media (min-width: $from-medium) { 15 | height: 100vh; 16 | min-height: rem(750px); 17 | } 18 | } 19 | 20 | .c-header_title { 21 | position: absolute; 22 | bottom: 0; 23 | left: 0; 24 | margin: 0; 25 | padding: rem($unit-small) 0; 26 | perspective: 600px; 27 | -webkit-perspective: 600px; 28 | } 29 | 30 | .c-header_line { 31 | position: absolute; 32 | bottom: 0; 33 | right: 0; 34 | left: 0; 35 | margin: 0; 36 | transform: scaleX(0); 37 | transition: transform 0.6s $easing; 38 | 39 | &.is-inview { 40 | transform: scaleX(1) 41 | } 42 | } 43 | 44 | .c-header_title_line { 45 | display: block; 46 | opacity: 0; 47 | transform-origin: center top; 48 | transform-style: preserve-3d; 49 | transform: translateY(100%) rotateX(-80deg); 50 | transition: opacity 0s $Power2EaseOut, transform 0s $Power2EaseOut; 51 | 52 | html.is-ready & { 53 | transform: none; 54 | opacity: 1; 55 | transition-duration: 0.8s; 56 | 57 | @for $i from 1 through 4 { 58 | &:nth-child(#{$i}){ 59 | transition-delay: $i*0.1s; 60 | } 61 | } 62 | } 63 | 64 | span { 65 | display: inline-block; 66 | } 67 | 68 | &.-version { 69 | text-transform: none; 70 | font-size: 0; 71 | } 72 | } 73 | 74 | 75 | 76 | .c-header_heading { 77 | padding: rem(30px) 0; 78 | } 79 | 80 | .c-header_heading_label { 81 | display: block; 82 | transition: transform 0s $easing; 83 | 84 | transform: translateY(-60px); 85 | 86 | html.is-ready & { 87 | transform: none; 88 | transition-duration: 0.6s; 89 | 90 | } 91 | 92 | html.is-ready .o-layout_item:nth-child(2) & { 93 | transition-delay: 0.1s; 94 | } 95 | } 96 | 97 | .c-header_logo { 98 | position: absolute; 99 | bottom: rem(40px); 100 | right: 0; 101 | opacity: 0; 102 | transform: translateY($unit * 2); 103 | transition: opacity 0s $Power2EaseOut, transform 0s $Power2EaseOut; 104 | 105 | @media (max-width: $to-small) { 106 | width: 30px; 107 | height: 66px; 108 | } 109 | 110 | @media (min-width: $from-small) { 111 | width: rem(64px); 112 | height: rem(140px); 113 | } 114 | 115 | svg { 116 | display: block; 117 | width: 100%; 118 | height: 100%; 119 | } 120 | 121 | html.is-ready & { 122 | transform: none; 123 | opacity: 1; 124 | transition-delay: 0.6s; 125 | transition-duration: 0.6s; 126 | 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /docs/src/styles/components/_helicopter.scss: -------------------------------------------------------------------------------- 1 | .c-helicopter_wrapper { 2 | height: 100%; 3 | position: relative; 4 | display: flex; 5 | } 6 | 7 | .c-helicopter { 8 | align-self: center; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /docs/src/styles/components/_intro.scss: -------------------------------------------------------------------------------- 1 | .c-intro { 2 | margin: rem($unit * 2) 0; 3 | font-size: rem(35px); 4 | line-height: 1.2; 5 | opacity: 0; 6 | transform: translateY($unit); 7 | transition: opacity 0.6s $easing, transform 0.6s $easing; 8 | 9 | &.is-inview { 10 | opacity: 1; 11 | transform: none; 12 | transition-delay: 0.3s; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docs/src/styles/components/_lerp-block.scss: -------------------------------------------------------------------------------- 1 | .c-lerp-block { 2 | font-weight: $bold; 3 | text-transform: uppercase; 4 | margin-bottom: rem(160px); 5 | 6 | @media (min-width: $from-medium) { 7 | &:not(:first-child) { 8 | margin-top: rem($unit); 9 | } 10 | } 11 | @media (max-width: $to-medium) { 12 | margin-top: rem($unit/2); 13 | } 14 | } 15 | 16 | .c-lerp-block_index { 17 | vertical-align: middle; 18 | color: $beige; 19 | width: rem(60px); 20 | height: rem(45px); 21 | display: inline-flex; 22 | align-items: center; 23 | justify-content: center; 24 | font-size: rem(20px); 25 | position: relative; 26 | z-index: 1; 27 | 28 | &::before { 29 | content:""; 30 | position: absolute; 31 | top: 0; 32 | bottom: 0; 33 | right: 0; 34 | left: 0; 35 | z-index: -1; 36 | background-color: $black; 37 | border-radius: 50%; 38 | transform: scale(0); 39 | transition: transform 0.6s $bounce; 40 | } 41 | 42 | &.is-inview { 43 | &::before { 44 | transform: scale(1); 45 | transition-delay: 0.3s; 46 | } 47 | } 48 | } 49 | 50 | .c-lerp-block_title { 51 | display: inline-block; 52 | vertical-align: middle; 53 | font-size: 0; 54 | margin-left: rem(20px); 55 | line-height: 1.1; 56 | 57 | &.is-inview { 58 | >span:not([data-scroll]) { 59 | opacity: 1; 60 | transform: none; 61 | transition-delay: 0.4s; 62 | } 63 | } 64 | 65 | >span:not([data-scroll]) { 66 | opacity: 0; 67 | transform: translateY(60px); 68 | transition: opacity 0.6s $easing, transform 0.6s $easing; 69 | } 70 | 71 | span { 72 | display: inline-block; 73 | min-width: 0.3em; 74 | 75 | @media (max-width: $to-small) { 76 | font-size: rem(25px); 77 | } 78 | 79 | @media (min-width: $from-small) { 80 | font-size: rem(70px); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /docs/src/styles/components/_section.scss: -------------------------------------------------------------------------------- 1 | .c-section { 2 | position: relative; 3 | 4 | @media (max-width: $to-large) { 5 | padding-bottom: 10vh; 6 | margin-bottom: 10vh; 7 | } 8 | 9 | @media (min-width: $from-large) { 10 | margin-bottom: 35vh; 11 | } 12 | } 13 | 14 | .c-section_infos { 15 | position: relative; 16 | max-width: 320px; 17 | z-index: 0; 18 | 19 | @media (min-width: $from-medium) { 20 | padding-top: rem($unit * 2); 21 | 22 | &.-padding { 23 | padding-top: 35vh; 24 | } 25 | } 26 | 27 | @media (max-width: $to-medium) { 28 | margin-bottom: rem($unit/2); 29 | } 30 | } 31 | 32 | .c-section_infos_inner { 33 | > * { 34 | opacity: 0; 35 | transform: translateY(60px); 36 | transition: opacity 0.6s $easing, transform 0.6s $easing; 37 | } 38 | 39 | &.is-inview { 40 | > * { 41 | opacity: 1; 42 | transform: none; 43 | 44 | @for $i from 1 through 3 { 45 | &:nth-child(#{$i}){ 46 | transition-delay: $i*0.06s; 47 | } 48 | } 49 | } 50 | } 51 | } 52 | 53 | .c-sections_infos_text { 54 | margin-top: rem(20px); 55 | } 56 | 57 | .c-image { 58 | opacity: 0; 59 | transition: opacity 0.6s $easing; 60 | 61 | &.is-inview { 62 | opacity: 1; 63 | transition-delay: 0.6s; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /docs/src/styles/components/_speed-block.scss: -------------------------------------------------------------------------------- 1 | .c-speed-block { 2 | position: relative; 3 | 4 | @media (min-width: $from-medium) { 5 | &.-margin { 6 | margin-top: rem($unit * 2); 7 | } 8 | } 9 | 10 | @media (max-width: $to-medium) { 11 | margin: rem($unit/2) 0; 12 | } 13 | @media (max-width: $to-small) { 14 | margin: $unit/4 0; 15 | } 16 | } 17 | 18 | .c-speed-block_image { 19 | margin-top: -30px; 20 | margin-bottom: -30px; 21 | } 22 | 23 | .c-speed-block_title { 24 | @include u-label; 25 | color: $white; 26 | text-align: center; 27 | position: absolute; 28 | bottom: rem($unit); 29 | right: 0; 30 | left: 0; 31 | opacity: 0; 32 | transform: translateY(60px); 33 | transition: opacity 0.6s $easing, transform 0.6s $easing; 34 | 35 | &.is-inview { 36 | opacity: 1; 37 | transform: none; 38 | transition-delay: 0.3s; 39 | } 40 | 41 | @media (max-width: $to-large) { 42 | display: none; 43 | } 44 | } 45 | 46 | .c-speed-block_bubble { 47 | position: absolute; 48 | right: 20px; 49 | top: 30%; 50 | z-index: 1; 51 | color: $beige; 52 | padding: rem($unit/2.5) rem($unit-small); 53 | font-size: rem(20px); 54 | 55 | &.-right { 56 | right: - 30px; 57 | } 58 | &.-left { 59 | right: auto; 60 | left: - 30px; 61 | } 62 | &.-top { 63 | top: -20%; 64 | } 65 | &.-bottom { 66 | top: auto; 67 | bottom: 0; 68 | } 69 | 70 | &::before { 71 | content:""; 72 | position: absolute; 73 | top: 0; 74 | bottom: 0; 75 | right: 0; 76 | left: 0; 77 | z-index: -1; 78 | background-color: $black; 79 | border-radius: 50%; 80 | transform: scale(0); 81 | transition: transform 0.6s $bounce; 82 | } 83 | 84 | &.is-inview { 85 | &::before { 86 | transform: scale(1); 87 | transition-delay: 0.3s; 88 | } 89 | } 90 | 91 | @media (max-width: $to-large) { 92 | display: none; 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /docs/src/styles/components/_summary.scss: -------------------------------------------------------------------------------- 1 | .c-summary { 2 | margin: rem($unit) 0 rem($unit * 2) 0; 3 | } 4 | 5 | .c-summary_text { 6 | font-size: rem(18px); 7 | font-weight: $semi-bold; 8 | opacity: 0; 9 | transform: translateY(60px); 10 | transition: transform 1s $easing; 11 | 12 | .c-summary.is-inview & { 13 | opacity: 1; 14 | transform: none; 15 | } 16 | 17 | @media (min-width: $from-medium) { 18 | max-width: 320px; 19 | } 20 | } 21 | 22 | .c-summary_list { 23 | @media (max-width: $to-medium) { 24 | margin-top: rem($unit/2); 25 | } 26 | } 27 | 28 | .c-summary_list_item { 29 | position: relative; 30 | opacity: 0; 31 | transform: translateY(60px); 32 | transition: opacity 0.6s $easing, transform 0.6s $easing; 33 | 34 | &::after { 35 | content:""; 36 | position: absolute; 37 | bottom: 0; 38 | right: 0; 39 | left: 0; 40 | height: 1px; 41 | background-color: $black; 42 | transform: scaleX(0); 43 | transform-origin: center left; 44 | transition: transform 0.6s $easing; 45 | } 46 | 47 | &.is-inview { 48 | transform: none; 49 | opacity: 1; 50 | 51 | &::after { 52 | transform: scaleX(1); 53 | } 54 | 55 | @for $i from 1 through 6 { 56 | &:nth-child(#{$i}){ 57 | transition-delay: $i*0.1s; 58 | &::after { 59 | transition-delay: 0.4 + $i*0.1s; 60 | } 61 | } 62 | } 63 | } 64 | 65 | a { 66 | display: block; 67 | padding: 8px 0; 68 | } 69 | } 70 | 71 | .c-summary_list_icon { 72 | position: absolute; 73 | top: 50%; 74 | right: 0; 75 | transform: translateY(-50%) translateX(30px) rotate(90deg); 76 | opacity: 0; 77 | transition: opacity 0.4s $easing, transform 0.4s $easing; 78 | 79 | .c-summary_list_item.is-inview & { 80 | transform: translateY(-50%) rotate(0deg); 81 | opacity: 1; 82 | } 83 | @for $i from 1 through 6 { 84 | .c-summary_list_item.is-inview:nth-child(#{$i}) &{ 85 | transition-delay: 0.5 + $i*0.1s; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /docs/src/styles/elements/_fonts.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Base / Fonts 3 | // ========================================================================== 4 | 5 | 6 | @font-face { 7 | font-family: 'Neue Montreal'; 8 | src: url('../fonts/NeueMontreal-Medium.woff2') format('woff2'), 9 | url('../fonts/NeueMontreal-Medium.woff') format('woff'); 10 | font-weight: 500; 11 | font-style: normal; 12 | } 13 | $font-neue-montreal: 'Neue Montreal'; 14 | 15 | $font-lucida : 'Lucida Grande'; 16 | -------------------------------------------------------------------------------- /docs/src/styles/elements/_headings.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Base / Headings 3 | // ========================================================================== 4 | 5 | @mixin h { 6 | margin-top: 0; 7 | line-height: $line-height-h; 8 | } 9 | 10 | // 11 | // Provide a generic class to apply common heading styles. 12 | // 13 | // @example 14 | //

15 | // 16 | // 17 | .o-h { 18 | @include h; 19 | margin-bottom: 0; 20 | line-height: 1; 21 | font-weight: $normal; 22 | } 23 | 24 | // 25 | // Styles for headings 1 through 6 with classes to provide 26 | // a double stranded heading hierarchy, e.g. we semantically 27 | // need an H2, but we want it to be sized like an H1: 28 | // 29 | // @example 30 | //

31 | // 32 | // 33 | h1, .o-h1 { 34 | @extend .o-h; 35 | 36 | line-height: 0.9; 37 | text-transform: uppercase; 38 | 39 | @media (max-width: $to-small) { 40 | font-size: 12vw; 41 | } 42 | 43 | @media (min-width: $from-small) and (max-width: $to-huge) { 44 | font-size: $font-size-h1; 45 | 46 | &.-huge { 47 | font-size: 36vh; 48 | } 49 | } 50 | 51 | @media (min-width: $from-huge) { 52 | font-size: 12vw; 53 | } 54 | } 55 | 56 | h2, .o-h2 { 57 | @extend .o-h; 58 | 59 | font-size: rem($font-size-h2); 60 | } 61 | 62 | h3, .o-h3 { 63 | @extend .o-h; 64 | 65 | font-size: rem($font-size-h3); 66 | } 67 | 68 | h4, .o-h4 { 69 | @extend .o-h; 70 | line-height: 1.2; 71 | 72 | font-size: rem($font-size-h4); 73 | } 74 | 75 | h5, .o-h5 { 76 | @extend .o-h; 77 | 78 | font-size: rem($font-size-h5); 79 | } 80 | 81 | h6, .o-h6 { 82 | @extend .o-h; 83 | 84 | font-size: rem($font-size-h6); 85 | } 86 | -------------------------------------------------------------------------------- /docs/src/styles/elements/_page.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Base / Page 3 | // ========================================================================== 4 | 5 | // 6 | // Simple page-level setup. 7 | // 8 | // 1. Set the default `font-size` and `line-height` for the entire project, 9 | // sourced from our default variables. 10 | // 2. Force scrollbars to always be visible to prevent awkward ‘jumps’ when 11 | // navigating between pages that do/do not have enough content to produce 12 | // scrollbars naturally. 13 | // 3. Ensure the page always fills at least the entire height of the viewport. 14 | // 15 | html { 16 | color: $color; 17 | font-family: $font-neue-montreal; 18 | line-height: $line-height; /* [1] */ 19 | 20 | @media (max-width: $to-small) { 21 | font-size: 12px; 22 | } 23 | 24 | @media (min-width: $from-small) and (max-width: $to-medium) { 25 | font-size: 13px; 26 | } 27 | 28 | @media (min-width: $from-medium) and (max-width: $to-large) { 29 | font-size: 14px; 30 | } 31 | 32 | @media (min-width: $from-large) and (max-width: $to-huge) { 33 | font-size: $font-size; /* [1] */ 34 | } 35 | 36 | @media (min-width: $from-huge) and (max-width: $to-gigantic) { 37 | font-size: 18px; 38 | } 39 | 40 | @media (min-width: $from-gigantic) and (max-width: $to-colossal) { 41 | font-size: 21px; 42 | } 43 | 44 | @media (min-width: $from-colossal) { 45 | font-size: 24px; 46 | } 47 | 48 | &:not(.has-scroll-init) { 49 | cursor: wait; 50 | overflow: hidden; 51 | } 52 | } 53 | 54 | ::selection { 55 | background-color: $selection-background-color; 56 | color: $selection-text-color; 57 | text-shadow: none; 58 | } 59 | 60 | a { 61 | transition: color 0.3s $easing; 62 | 63 | @media (min-width: $from-large) { 64 | @include u-hocus { 65 | color: $link-hover-color; 66 | } 67 | } 68 | 69 | color: $link-color; 70 | 71 | &.-underline { 72 | text-decoration: underline; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /docs/src/styles/generic/_button.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Generic / Buttons 3 | // ========================================================================== 4 | 5 | // 6 | // 1. Allow us to style box model properties. 7 | // 2. Fixes odd inner spacing in IE7. 8 | // 3. Reset/normalize some styles. 9 | // 4. Line different sized buttons up a little nicer. 10 | // 5. Make buttons inherit font styles (often necessary when styling `input`s as buttons). 11 | // 6. Force all button-styled elements to appear clickable. 12 | // 13 | button, 14 | .o-button { 15 | @include u-hocus { 16 | text-decoration: none; 17 | } 18 | 19 | display: inline-block; /* [1] */ 20 | overflow: visible; /* [2] */ 21 | margin: 0; /* [3] */ 22 | padding: 0; 23 | outline: 0; 24 | border: 0; 25 | background: none transparent; 26 | color: inherit; 27 | vertical-align: middle; /* [4] */ 28 | text-align: center; /* [3] */ 29 | text-decoration: none; 30 | text-transform: none; 31 | font: inherit; /* [5] */ 32 | line-height: normal; 33 | cursor: pointer; /* [6] */ 34 | user-select: none; 35 | } 36 | -------------------------------------------------------------------------------- /docs/src/styles/generic/_form.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Generic / Forms 3 | // ========================================================================== 4 | 5 | input, 6 | select, 7 | textarea { 8 | display: block; 9 | margin: 0; 10 | padding: 0; 11 | width: 100%; 12 | outline: 0; 13 | border: 0; 14 | border-radius: 0; 15 | background: none transparent; 16 | color: inherit; 17 | font: inherit; 18 | line-height: normal; 19 | appearance: none; 20 | } 21 | 22 | select { 23 | text-transform: none; 24 | 25 | &::-ms-expand { 26 | display: none; 27 | } 28 | 29 | &::-ms-value { 30 | background: none; 31 | color: inherit; 32 | } 33 | 34 | // Remove Firefox :focus dotted outline, breaks color inherit 35 | // &:-moz-focusring { 36 | // color: transparent; 37 | // text-shadow: 0 0 0 #000000; // Text :focus color 38 | // } 39 | } 40 | 41 | textarea { 42 | overflow: auto; 43 | resize: vertical; 44 | } 45 | -------------------------------------------------------------------------------- /docs/src/styles/generic/_generic.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Generic 3 | // ========================================================================== 4 | 5 | html { 6 | box-sizing: border-box; 7 | } 8 | 9 | // 10 | // Add the correct display in IE 10-. 11 | // 1. Add the correct display in IE. 12 | // 13 | template, /* [1] */ 14 | [hidden] { 15 | display: none; 16 | } 17 | 18 | *, 19 | :before, 20 | :after { 21 | box-sizing: inherit; 22 | } 23 | 24 | address { 25 | font-style: inherit; 26 | } 27 | 28 | dfn, 29 | cite, 30 | em, 31 | i { 32 | font-style: italic; 33 | } 34 | 35 | b, 36 | strong { 37 | font-weight: $bold; 38 | } 39 | 40 | a { 41 | text-decoration: none; 42 | 43 | svg { 44 | pointer-events: none; 45 | } 46 | } 47 | 48 | // Resets 49 | ul, 50 | ol { 51 | margin: 0; 52 | padding: 0; 53 | list-style: none; 54 | } 55 | 56 | p, 57 | figure { 58 | margin: 0; 59 | padding: 0; 60 | } 61 | 62 | /** 63 | * 1. Single taps should be dispatched immediately on clickable elements 64 | */ 65 | a, area, button, input, label, select, textarea, [tabindex] { 66 | -ms-touch-action: manipulation; /* [1] */ 67 | touch-action: manipulation; 68 | } 69 | 70 | [hreflang] > abbr[title] { 71 | text-decoration: none; 72 | } 73 | 74 | table { 75 | border-spacing: 0; 76 | border-collapse: collapse; 77 | } 78 | 79 | hr { 80 | display: block; 81 | margin: 1em 0; 82 | padding: 0; 83 | height: 1px; 84 | border: 0; 85 | border-top: 1px solid $black; 86 | } 87 | -------------------------------------------------------------------------------- /docs/src/styles/generic/_media.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Generic / Media 3 | // ========================================================================== 4 | 5 | // 6 | // 1. Setting `vertical-align` removes the whitespace that appears under `img` 7 | // elements when they are dropped into a page as-is. Safer alternative to 8 | // using `display: block;`. 9 | // 10 | audio, 11 | canvas, 12 | iframe, 13 | img, 14 | svg, 15 | video { 16 | vertical-align: middle; /* [1] */ 17 | } 18 | 19 | // 20 | // Add the correct display in iOS 4-7. 21 | // 22 | audio:not([controls]) { 23 | display: none; 24 | height: 0; 25 | } 26 | 27 | // 28 | // 2. Fluid media for responsive purposes. 29 | // 30 | img, 31 | svg { 32 | max-width: 100%; /* [2] */ 33 | height: auto; 34 | 35 | // 36 | // 4. If a `width` and/or `height` attribute have been explicitly defined, let’s 37 | // not make the image fluid. 38 | // 39 | &[width], /* [4] */ 40 | &[height] { 41 | /* [4] */ 42 | max-width: none; 43 | } 44 | } 45 | 46 | // 47 | // 4. Offset `alt` text from surrounding copy. 48 | // 49 | img { 50 | font-style: italic; /* [4] */ 51 | } 52 | 53 | // 54 | // 5. SVG elements should fallback to their surrounding text color. 55 | // 56 | svg { 57 | fill: currentColor; /* [5] */ 58 | } 59 | -------------------------------------------------------------------------------- /docs/src/styles/main.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Main 3 | // ========================================================================== 4 | 5 | // Settings 6 | // ========================================================================== 7 | @import "settings/config.colors"; 8 | @import "settings/config"; 9 | 10 | // ========================================================================== 11 | // Tools 12 | // ========================================================================== 13 | @import "tools/functions"; 14 | @import "tools/mixins"; 15 | @import "tools/fonts"; 16 | @import "tools/layout"; 17 | @import "tools/widths"; 18 | // @import "tools/family"; 19 | 20 | // Generic 21 | // ========================================================================== 22 | @import "node_modules/normalize.css/normalize"; 23 | @import "generic/generic"; 24 | @import "generic/media"; 25 | @import "generic/form"; 26 | @import "generic/button"; 27 | 28 | // Elements 29 | // ========================================================================== 30 | @import "elements/fonts"; 31 | @import "elements/page"; 32 | @import "elements/headings"; 33 | 34 | // Objects 35 | // ========================================================================== 36 | @import "objects/container"; 37 | @import "objects/ratio"; 38 | @import "objects/layout"; 39 | @import "objects/form"; 40 | @import "objects/button"; 41 | @import "objects/scroll"; 42 | @import "objects/title"; 43 | @import "objects/image"; 44 | // @import "objects/crop"; 45 | // @import "objects/table"; 46 | 47 | // Vendors 48 | // ========================================================================== 49 | @import "../../../src/locomotive-scroll"; 50 | 51 | // Components 52 | // ========================================================================== 53 | @import "components/header"; 54 | @import "components/fixed"; 55 | @import "components/intro"; 56 | @import "components/features"; 57 | @import "components/summary"; 58 | @import "components/section"; 59 | @import "components/speed-block"; 60 | @import "components/direction-block"; 61 | @import "components/lerp-block"; 62 | @import "components/cta"; 63 | @import "components/choochoo"; 64 | @import "components/damn"; 65 | @import "components/helicopter"; 66 | 67 | // Templates 68 | // ========================================================================== 69 | // @import "templates/template"; 70 | 71 | // Utilities 72 | // ========================================================================== 73 | @import "utilities/ratio"; 74 | @import "utilities/widths"; 75 | @import "utilities/align"; 76 | @import "utilities/helpers"; 77 | // @import "utilities/states"; 78 | // @import "utilities/headings"; 79 | // @import "utilities/spacing"; 80 | // @import "utilities/print"; 81 | -------------------------------------------------------------------------------- /docs/src/styles/objects/_button.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Objects / Buttons 3 | // ========================================================================== 4 | .o-button { 5 | position: relative; 6 | z-index: 1; 7 | color: white; 8 | padding: rem(32px) rem(35px); 9 | font-size: rem(24px); 10 | background-color: $black; 11 | transition: color 0.3s $easing; 12 | width: 100%; 13 | text-align: left; 14 | 15 | &::before{ 16 | content:""; 17 | position: absolute; 18 | top: 0; 19 | bottom: 0; 20 | right: 0; 21 | left: 0; 22 | z-index: -1; 23 | transform: scaleY(0); 24 | transform-origin: center top; 25 | background-color: $white; 26 | transition: transform 0.3s $easing; 27 | } 28 | 29 | 30 | @include u-hocus { 31 | color: $black; 32 | 33 | &::before { 34 | transform: scaleY(1); 35 | transform-origin: center bottom; 36 | } 37 | } 38 | } 39 | 40 | .o-button_icon { 41 | display: inline-block; 42 | vertical-align: middle; 43 | width: rem(55px); 44 | height: rem(55px); 45 | margin-right: rem($unit / 4); 46 | 47 | svg { 48 | display: block; 49 | width: 100%; 50 | height: 100%; 51 | } 52 | } 53 | 54 | .o-button_arrow { 55 | position: absolute; 56 | top: 50%; 57 | right: rem(35px); 58 | transform: translateY(-50%); 59 | } 60 | -------------------------------------------------------------------------------- /docs/src/styles/objects/_container.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Objects / Container 3 | // ========================================================================== 4 | 5 | // 6 | // Page-level constraining and wrapping elements. 7 | // 8 | // > In programming languages the word *container* is generally used for structures 9 | // that can contain more than one element. 10 | // > A *wrapper* instead is something that wraps around a single object to provide 11 | // more functionalities and interfaces to it. 12 | // @link http://stackoverflow.com/a/13202141/140357 13 | // 14 | 15 | /* stylelint-disable */ 16 | @if (type-of($container-width) != number) { 17 | @error "`#{$container-width}` needs to be a number." 18 | } 19 | /* stylelint-enable */ 20 | 21 | .o-container { 22 | margin-right: auto; 23 | margin-left: auto; 24 | max-width: rem($container-width + (40px*2)); 25 | 26 | @media (max-width: $to-small) { 27 | padding-right: 20px; 28 | padding-left: 20px; 29 | } 30 | 31 | @media (min-width: $from-small) { 32 | padding-right: rem(40px); 33 | padding-left: rem(40px); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /docs/src/styles/objects/_crop.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Objects / Crop 3 | // ========================================================================== 4 | 5 | // 6 | // @link https://github.com/inuitcss/inuitcss/blob/19d0c7e/objects/_objects.crop.scss 7 | // 8 | 9 | // A list of cropping ratios that get generated as modifier classes. 10 | $crop-ratios: ( 11 | (2:1), 12 | (4:3), 13 | (16:9), 14 | ) !default; 15 | 16 | /** 17 | * Provide a cropping container in order to display media (usually images) 18 | * cropped to certain ratios. 19 | * 20 | * 1. Set up a positioning context in which the image can sit. 21 | * 2. This is the crucial part: where the cropping happens. 22 | */ 23 | .o-crop { 24 | position: relative; /* [1] */ 25 | display: block; 26 | overflow: hidden; /* [2] */ 27 | } 28 | 29 | /** 30 | * Apply this class to the content (usually `img`) that needs cropping. 31 | * 32 | * 1. Image’s default positioning is top-left in the cropping box. 33 | * 2. Make sure the media doesn’t stop itself too soon. 34 | */ 35 | .o-crop_content { 36 | position: absolute; 37 | top: 0; /* [1] */ 38 | left: 0; /* [1] */ 39 | max-width: none; /* [2] */ 40 | 41 | /** 42 | * We can position the media in different locations within the cropping area. 43 | */ 44 | &.-right { 45 | right: 0; 46 | left: auto; 47 | } 48 | 49 | &.-bottom { 50 | top: auto; 51 | bottom: 0; 52 | } 53 | 54 | &.-center { 55 | top: 50%; 56 | left: 50%; 57 | transform: translate(-50%, -50%); 58 | } 59 | } 60 | 61 | /* stylelint-disable */ 62 | 63 | // 64 | // Generate a series of crop classes to be used like so: 65 | // 66 | // @example 67 | //
68 | // 69 | // 70 | .o-crop { 71 | @each $crop in $crop-ratios { 72 | @each $antecedent, $consequent in $crop { 73 | @if (type-of($antecedent) != number) { 74 | @error "`#{$antecedent}` needs to be a number." 75 | } 76 | 77 | @if (type-of($consequent) != number) { 78 | @error "`#{$consequent}` needs to be a number." 79 | } 80 | 81 | &.-#{$antecedent}\:#{$consequent} { 82 | padding-bottom: ($consequent/$antecedent) * 100%; 83 | } 84 | } 85 | } 86 | } 87 | 88 | /* stylelint-enable */ 89 | -------------------------------------------------------------------------------- /docs/src/styles/objects/_form.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Objects / Buttons 3 | // ========================================================================== 4 | 5 | // Label 6 | // ============================================================================= 7 | .o-label { 8 | display: block; 9 | margin-bottom: rem(15px); 10 | } 11 | 12 | // Input 13 | // ============================================================================= 14 | $input-icon-color: 424242; // No # 15 | 16 | .o-input { 17 | padding: rem(10px); 18 | border-width: 1px; 19 | border-style: solid; 20 | border-color: lightgray; 21 | background-color: white; 22 | 23 | &:focus { 24 | border-color: gray; 25 | } 26 | 27 | &::placeholder { 28 | color: gray; 29 | } 30 | } 31 | 32 | // Checkbox 33 | // ============================================================================= 34 | $checkbox: rem(18px); 35 | $checkbox-icon-color: $input-icon-color; 36 | 37 | .o-checkbox { 38 | position: absolute; 39 | width: 0; 40 | opacity: 0; 41 | 42 | &:focus { 43 | + .o-checkbox-label { 44 | &::before { 45 | border-color: gray; 46 | } 47 | } 48 | } 49 | 50 | &:checked { 51 | + .o-checkbox-label { 52 | &::after { 53 | opacity: 1; 54 | } 55 | } 56 | } 57 | } 58 | 59 | .o-checkbox-label { 60 | @extend .o-label; 61 | 62 | position: relative; 63 | display: inline-block; 64 | margin-right: 0.5em; 65 | padding-left: ($checkbox + rem(10px)); 66 | 67 | &::before, &::after { 68 | 69 | position: absolute; 70 | top: 50%; 71 | left: 0; 72 | display: inline-block; 73 | margin-top: (-$checkbox / 2); 74 | padding: 0; 75 | width: $checkbox; 76 | height: $checkbox; 77 | content: ""; 78 | } 79 | 80 | &::before { 81 | background-color: $white; 82 | } 83 | 84 | &::after { 85 | border-color: transparent; 86 | background-color: transparent; 87 | 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"); 88 | background-position: center; 89 | background-size: rem(13px); 90 | background-repeat: no-repeat; 91 | opacity: 0; 92 | } 93 | } 94 | 95 | // Radio 96 | // ============================================================================= 97 | $radio-icon-color: $input-icon-color; 98 | 99 | .o-radio { 100 | @extend .o-checkbox; 101 | } 102 | 103 | .o-radio-label { 104 | @extend .o-checkbox-label; 105 | 106 | &::before, &::after { 107 | border-radius: 50%; 108 | } 109 | 110 | &::after { 111 | 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"); 112 | background-size: rem(8px); 113 | } 114 | } 115 | 116 | // Select 117 | // ============================================================================= 118 | $select-icon: rem(40px); 119 | $select-icon-color: $input-icon-color; 120 | 121 | .o-select { 122 | @extend .o-input; 123 | 124 | position: relative; 125 | z-index: 1; 126 | padding-right: $select-icon; 127 | } 128 | 129 | .o-select-wrap { 130 | position: relative; 131 | 132 | &::after { 133 | position: absolute; 134 | top: 0; 135 | right: 0; 136 | bottom: 0; 137 | z-index: 2; 138 | width: $select-icon; 139 | 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"); 140 | background-position: center; 141 | background-size: rem(10px); 142 | background-repeat: no-repeat; 143 | content: ""; 144 | pointer-events: none; 145 | } 146 | } 147 | 148 | // Textarea 149 | // ============================================================================= 150 | .o-textarea { 151 | @extend .o-input; 152 | 153 | min-height: rem(100px); 154 | } 155 | -------------------------------------------------------------------------------- /docs/src/styles/objects/_image.scss: -------------------------------------------------------------------------------- 1 | .o-image_wrapper { 2 | position: relative; 3 | overflow: hidden; 4 | background-color: hsl(0,11%, 81%); 5 | 6 | &.-full { 7 | height: 100%; 8 | margin: 0 rem(100px); 9 | display: flex; 10 | align-content: center; 11 | 12 | 13 | @media (max-width: $to-small) { 14 | margin: rem($unit/2) 0; 15 | } 16 | } 17 | } 18 | 19 | .o-image { 20 | opacity: 0.75; 21 | mix-blend-mode: multiply; 22 | 23 | img { 24 | width: 100%; 25 | opacity: 0; 26 | transform: scale(1.4); 27 | transition: opacity 1.2s $Power2EaseOut, transform 1.2s $Power2EaseOut; 28 | } 29 | 30 | &.is-inview { 31 | img { 32 | opacity: 1; 33 | transform: scale(1); 34 | transition-delay: 0.6s; 35 | } 36 | } 37 | 38 | .o-image_wrapper.-full & { 39 | align-self: center; 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /docs/src/styles/objects/_layout.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Objects / Layout 3 | // ========================================================================== 4 | 5 | // 6 | // Grid-like layout system. 7 | // 8 | // The layout object provides us with a column-style layout system. This file 9 | // contains the basic structural elements, but classes should be complemented 10 | // with width utilities, for example: 11 | // 12 | // @example 13 | //
14 | //
15 | //
16 | //
17 | //
18 | //
19 | //
20 | //
21 | // 22 | // We can also manipulate entire layout systems by adding a series of modifiers 23 | // to the `.o-layout` block. For example: 24 | // 25 | // @example 26 | //
27 | // 28 | // This will reverse the displayed order of the system so that it runs in the 29 | // opposite order to our source, effectively flipping the system over. 30 | // 31 | // @example 32 | //
33 | // 34 | // This will cause the system to fill up from either the centre or the right 35 | // hand side. Default behaviour is to fill up the layout system from the left. 36 | // 37 | // @requires tools/layout 38 | // @link https://github.com/inuitcss/inuitcss/blob/0420ba8/objects/_objects.layout.scss 39 | // 40 | 41 | .o-layout { 42 | @include o-layout; 43 | 44 | // Gutter modifiers 45 | &.-gutter { 46 | margin-left: rem(-$unit); 47 | } 48 | 49 | &.-gutter-small { 50 | margin-left: rem(-$unit/2); 51 | } 52 | 53 | // Horizontal alignment modifiers 54 | &.-center { 55 | text-align: center; 56 | } 57 | 58 | &.-right { 59 | text-align: right; 60 | } 61 | 62 | &.-reverse { 63 | direction: rtl; 64 | 65 | &.-flex { 66 | flex-direction: row-reverse; 67 | } 68 | } 69 | 70 | &.-flex { 71 | display: flex; 72 | 73 | &.-top { 74 | align-items: flex-start; 75 | } 76 | &.-middle { 77 | align-items: center; 78 | } 79 | &.-bottom { 80 | align-items: flex-end; 81 | } 82 | } 83 | &.-stretch { 84 | align-items: stretch; 85 | } 86 | } 87 | 88 | .o-layout_item { 89 | @include o-layout_item; 90 | 91 | // Gutter modifiers 92 | .o-layout.-gutter > & { 93 | padding-left: rem($unit); 94 | } 95 | 96 | .o-layout.-gutter-small > & { 97 | padding-left: rem($unit/2); 98 | } 99 | 100 | // Vertical alignment modifiers 101 | .o-layout.-middle > & { 102 | vertical-align: middle; 103 | } 104 | 105 | .o-layout.-bottom > & { 106 | vertical-align: bottom; 107 | } 108 | 109 | // Horizontal alignment modifiers 110 | .o-layout.-center > &, 111 | .o-layout.-right > &, 112 | .o-layout.-reverse > & { 113 | text-align: left; 114 | } 115 | 116 | .o-layout.-reverse > & { 117 | direction: ltr; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /docs/src/styles/objects/_ratio.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Objects / Ratio 3 | // ========================================================================== 4 | 5 | /** 6 | * Create ratio-bound content blocks, to keep media (e.g. images, videos) in 7 | * their correct aspect ratios. 8 | * 9 | * http://alistapart.com/article/creating-intrinsic-ratios-for-video 10 | * 11 | * 1. Default cropping is a 1:1 ratio (i.e. a perfect square). 12 | */ 13 | .o-ratio { 14 | position: relative; 15 | display: block; 16 | overflow: hidden; 17 | 18 | &:before { 19 | display: block; 20 | padding-bottom: 100%; /* [1] */ 21 | width: 100%; 22 | content: ""; 23 | } 24 | } 25 | 26 | .o-ratio_content, 27 | .o-ratio > img, 28 | .o-ratio > iframe, 29 | .o-ratio > embed, 30 | .o-ratio > object { 31 | position: absolute; 32 | top: 0; 33 | bottom: 0; 34 | left: 0; 35 | width: 100%; 36 | // height: 100%; 37 | } 38 | -------------------------------------------------------------------------------- /docs/src/styles/objects/_scroll.scss: -------------------------------------------------------------------------------- 1 | .o-scroll { 2 | background-color: hsl(0,11%, 81%); 3 | overflow: hidden; 4 | } 5 | -------------------------------------------------------------------------------- /docs/src/styles/objects/_table.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Objects / Tables 3 | // ========================================================================== 4 | .o-table { 5 | width: 100%; 6 | 7 | /** 8 | * Force all cells within a table to occupy the same width as each other. 9 | * 10 | * @link https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout#Values 11 | */ 12 | &.-fixed { 13 | table-layout: fixed; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /docs/src/styles/objects/_title.scss: -------------------------------------------------------------------------------- 1 | .o-title { 2 | margin: 0; 3 | padding: rem($unit-small) 0; 4 | perspective: 600px; 5 | -webkit-perspective: 600px; 6 | } 7 | 8 | 9 | .o-title_line { 10 | display: block; 11 | opacity: 0; 12 | transform-origin: center top; 13 | transform-style: preserve-3d; 14 | transform: translateY(100%) rotateX(-80deg); 15 | transition: opacity 0.8s $Power2EaseOut, transform 0.8s $Power2EaseOut; 16 | font-size: 0; 17 | 18 | .o-title.is-inview & { 19 | transform: none; 20 | opacity: 1; 21 | 22 | @for $i from 1 through 4 { 23 | &:nth-child(#{$i}){ 24 | transition-delay: 0.3 + $i*0.1s; 25 | } 26 | } 27 | } 28 | 29 | span { 30 | display: inline-block; 31 | min-width: 0.05em; 32 | font-size: $font-size-h1; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /docs/src/styles/settings/_config.colors.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Settings / Config / Colors 3 | // ========================================================================== 4 | 5 | // Palette 6 | // ============================================================================= 7 | $white: #FFFFFF; 8 | $black: #272727; 9 | 10 | // Specific 11 | // ============================================================================= 12 | // Link 13 | $link-color: $black; 14 | $link-focus-color: $black; 15 | $link-hover-color: $white; 16 | // Selection 17 | $selection-text-color: $white; 18 | $selection-background-color: $black; 19 | 20 | // Social Colors 21 | // ============================================================================= 22 | $facebook-color: #3B5998; 23 | $instagram-color: #E1306C; 24 | $youtube-color: #CD201F; 25 | $twitter-color: #1DA1F2; 26 | 27 | // Customs 28 | $beige: #D4C9C9; 29 | -------------------------------------------------------------------------------- /docs/src/styles/settings/_config.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Settings / Config 3 | // ========================================================================== 4 | 5 | // Context 6 | // ============================================================================= 7 | // The current stylesheet context. Available values: frontend, editor. 8 | $context: frontend !default; 9 | 10 | // Path is relative to the stylesheets directory. 11 | $assets-path: "../" !default; 12 | 13 | // Typefaces 14 | // ============================================================================= 15 | $font-sans-serif: Arial, sans-serif; 16 | 17 | // Typography 18 | // ============================================================================= 19 | // Base 20 | $font-size: 16px; 21 | $line-height: 24px / $font-size; 22 | $font-family: $font-sans-serif; 23 | $color: #222222; 24 | // Headings 25 | $font-size-h1: 13.5vw !default; 26 | $font-size-h2: 32px !default; 27 | $font-size-h3: 24px !default; 28 | $font-size-h4: 16px !default; 29 | $font-size-h5: 16px !default; 30 | $font-size-h6: 15px !default; 31 | $line-height-h: $line-height; 32 | // Weights 33 | $light: 300; 34 | $normal: 400; 35 | $medium: 500; 36 | $semi-bold: 600; 37 | $bold: 700; 38 | 39 | // Transitions 40 | // ============================================================================= 41 | $speed: 0.3s; 42 | $bounce: cubic-bezier(0.17, 0.67, 0.3, 1.33); 43 | $Power1EaseOut: cubic-bezier(0.250, 0.460, 0.450, 0.940); 44 | $Power2EaseOut: cubic-bezier(0.215, 0.610, 0.355, 1.000); 45 | $Power3EaseOut: cubic-bezier(0.165, 0.840, 0.440, 1.000); 46 | $Power4EaseOut: cubic-bezier(0.230, 1.000, 0.320, 1.000); 47 | $Power1EaseIn: cubic-bezier(0.550, 0.085, 0.680, 0.530) ; 48 | $Power2EaseIn: cubic-bezier(0.550, 0.055, 0.675, 0.190); 49 | $Power3EaseIn: cubic-bezier(0.895, 0.030, 0.685, 0.220); 50 | $Power4EaseIn: cubic-bezier(0.755, 0.050, 0.855, 0.060); 51 | $ExpoEaseOut: cubic-bezier(0.190, 1.000, 0.220, 1.000); 52 | $ExpoEaseIn: cubic-bezier(0.950, 0.050, 0.795, 0.035); 53 | $ExpoEaseInOut: cubic-bezier(1.000, 0.000, 0.000, 1.000); 54 | $SineEaseOut: cubic-bezier(0.390, 0.575, 0.565, 1.000); 55 | $SineEaseIn: cubic-bezier(0.470, 0.000, 0.745, 0.715); 56 | $Power1EaseInOut: cubic-bezier(0.455, 0.030, 0.515, 0.955); 57 | $Power2EaseInOut: cubic-bezier(0.645, 0.045, 0.355, 1.000); 58 | $Power3EaseInOut: cubic-bezier(0.770, 0.000, 0.175, 1.000); 59 | $Power4EaseInOut: cubic-bezier(0.860, 0.000, 0.070, 1.000); 60 | $SlowEaseOut: cubic-bezier(.04,1.15,0.4,.99); 61 | $easing: $Power2EaseOut; 62 | 63 | 64 | // Spacing Units 65 | // ============================================================================= 66 | $unit: 60px; 67 | $unit-small: 40px; 68 | 69 | // Container 70 | // ========================================================================== 71 | $container-width: 1440px; 72 | $padding: $unit; 73 | 74 | // Breakpoints 75 | // ============================================================================= 76 | $from-tiny: 500px !default; 77 | $to-tiny: $from-tiny - 1 !default; 78 | $from-small: 700px !default; 79 | $to-small: $from-small - 1 !default; 80 | $from-medium: 1000px !default; 81 | $to-medium: $from-medium - 1 !default; 82 | $from-large: 1200px !default; 83 | $to-large: $from-large - 1 !default; 84 | $from-big: 1400px !default; 85 | $to-big: $from-big - 1 !default; 86 | $from-huge: 1600px !default; 87 | $to-huge: $from-huge - 1 !default; 88 | $from-enormous: 1800px !default; 89 | $to-enormous: $from-enormous - 1 !default; 90 | $from-gigantic: 2000px !default; 91 | $to-gigantic: $from-gigantic - 1 !default; 92 | $from-colossal: 2400px !default; 93 | $to-colossal: $from-colossal - 1 !default; 94 | -------------------------------------------------------------------------------- /docs/src/styles/templates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/locomotivemtl/locomotive-scroll/8ac3990434182eb3e782edeae3d16e1cdf3ac015/docs/src/styles/templates/.gitkeep -------------------------------------------------------------------------------- /docs/src/styles/tools/_family.scss: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | // DOCS : https://lukyvj.github.io/family.scss/ 4 | // 5 | // 6 | /// Select all children from the first to `$num`. 7 | /// @group with-arguments 8 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 9 | /// @param {number} $num - id of the child 10 | @mixin first($num) { 11 | @if $num == 1 { 12 | &:first-child { 13 | @content; 14 | } 15 | } @else { 16 | &:nth-child(-n + #{$num}) { 17 | @content; 18 | } 19 | } 20 | } 21 | 22 | /// Select all children from the last to `$num`. 23 | /// @group with-arguments 24 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 25 | /// @param {number} $num - id of the child 26 | @mixin last($num) { 27 | &:nth-last-child(-n + #{$num}) { 28 | @content; 29 | } 30 | } 31 | 32 | /// Select all children after the first to `$num`. 33 | /// @group with-arguments 34 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 35 | /// @param {number} $num - id of the child 36 | @mixin after-first($num) { 37 | &:nth-child(n + #{$num + 1}) { 38 | @content; 39 | } 40 | } 41 | 42 | /// Select all children before `$num` from the last. 43 | /// @group with-arguments 44 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 45 | /// @param {number} $num - id of the child 46 | @mixin from-end($num) { 47 | &:nth-last-child(#{$num}) { 48 | @content; 49 | } 50 | } 51 | 52 | /// Select all children between `$first` and `$last`. 53 | /// @group with-arguments 54 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 55 | @mixin between($first, $last) { 56 | &:nth-child(n + #{$first}):nth-child(-n + #{$last}) { 57 | @content; 58 | } 59 | } 60 | 61 | /// Select all even children between `$first` and `$last`. 62 | /// @group with-arguments 63 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 64 | @mixin even-between($first, $last) { 65 | &:nth-child(even):nth-child(n + #{$first}):nth-child(-n + #{$last}) { 66 | @content; 67 | } 68 | } 69 | 70 | /// Select all odd children between `$first` and `$last`. 71 | /// @group with-arguments 72 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 73 | @mixin odd-between($first, $last) { 74 | &:nth-child(odd):nth-child(n + #{$first}):nth-child(-n + #{$last}) { 75 | @content; 76 | } 77 | } 78 | 79 | /// Select all `$num` children between `$first` and `$last`. 80 | /// @group with-arguments 81 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 82 | @mixin n-between($num, $first, $last) { 83 | &:nth-child(#{$num}n):nth-child(n + #{$first}):nth-child(-n + #{$last}) { 84 | @content; 85 | } 86 | } 87 | 88 | 89 | /// Select all children but `$num`. 90 | /// @group with-arguments 91 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 92 | /// @param {number} $num - id of the child 93 | @mixin all-but($num) { 94 | &:not(:nth-child(#{$num})) { 95 | @content; 96 | } 97 | } 98 | 99 | /// Select children each `$num`. 100 | /// @group with-arguments 101 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 102 | /// @param {number} $num - id of the child 103 | /// @alias every 104 | @mixin each($num) { 105 | &:nth-child(#{$num}n) { 106 | @content; 107 | } 108 | } 109 | 110 | /// Select children each `$num`. 111 | /// @group with-arguments 112 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 113 | /// @param {number} $num - id of the child 114 | @mixin every($num) { 115 | &:nth-child(#{$num}n) { 116 | @content; 117 | } 118 | } 119 | 120 | /// Select the `$num` child from the start and the `$num` child from the last. 121 | /// @group with-arguments 122 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 123 | /// @param {number} $num - id of the child 124 | @mixin from-first-last($num) { 125 | &:nth-child(#{$num}), 126 | &:nth-last-child(#{$num}) { 127 | @content; 128 | } 129 | } 130 | 131 | 132 | /// Select the item in the middle of `$num` child. Only works with odd number 133 | /// chain. 134 | /// @group with-arguments 135 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 136 | /// @param {number} $num - id of the child 137 | @mixin middle($num) { 138 | &:nth-child(#{round($num / 2)}) { 139 | @content; 140 | } 141 | } 142 | 143 | 144 | /// Select all children between the `$num` first and the `$num` last. 145 | /// @group with-arguments 146 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 147 | /// @param {number} $num - id of the child 148 | @mixin all-but-first-last($num) { 149 | &:nth-child(n + #{$num}):nth-last-child(n + #{$num}) { 150 | @content; 151 | } 152 | } 153 | 154 | 155 | /// This quantity-query mixin will only select the first of `$limit` items. It will not 156 | /// work if there is not as much as item as you set in `$limit`. 157 | /// @group Quantity queries 158 | /// @param {number} $limit 159 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 160 | @mixin first-of($limit) { 161 | &:nth-last-child(#{$limit}):first-child { 162 | @content; 163 | } 164 | } 165 | 166 | /// This quantity-query mixin will only select the last of `$limit` items. It will not 167 | /// if there is not as much as item as you set in `$limit`. 168 | /// @group Quantity queries 169 | /// @param {number} $limit 170 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 171 | @mixin last-of($limit) { 172 | &:nth-of-type(#{$limit}):nth-last-of-type(1) { 173 | @content; 174 | } 175 | } 176 | 177 | /// This quantity-query mixin will select every items if there is at least `$num` items. It will not 178 | /// if there is not as much as item as you set in `$num`. 179 | /// @group Quantity queries 180 | /// @param {number} $limit 181 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 182 | @mixin at-least($num) { 183 | $selector: &; 184 | $child: nth(nth($selector, -1), -1); 185 | 186 | &:nth-last-child(n + #{$num}), 187 | &:nth-last-child(n + #{$num}) ~ #{$child} { 188 | @content; 189 | } 190 | } 191 | 192 | /// This quantity-query mixin will select every items if there is at most `$num` items. It will not 193 | /// if there is not as much as item as you set in `$num`. 194 | /// @group Quantity queries 195 | /// @param {number} $limit 196 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 197 | @mixin at-most($num) { 198 | $selector: &; 199 | $child: nth(nth($selector, -1), -1); 200 | 201 | &:nth-last-child(-n + #{$num}):first-child, 202 | &:nth-last-child(-n + #{$num}):first-child ~ #{$child} { 203 | @content; 204 | } 205 | } 206 | 207 | /// This quantity-query mixin will select every items only if there is between `$min` and `$max` items. 208 | /// @group Quantity queries 209 | /// @param {number} $limit 210 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 211 | @mixin in-between($min, $max) { 212 | $selector: &; 213 | $child: nth(nth($selector, -1), -1); 214 | 215 | &:nth-last-child(n + #{$min}):nth-last-child(-n + #{$max}):first-child, 216 | &:nth-last-child(n + #{$min}):nth-last-child(-n + #{$max}):first-child ~ #{$child} { 217 | @content; 218 | } 219 | } 220 | 221 | /// Select the first exact child 222 | /// @group no-arguments 223 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 224 | @mixin first-child() { 225 | &:first-of-type { 226 | @content 227 | } 228 | } 229 | 230 | /// Select the last exact child 231 | /// @group no-arguments 232 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 233 | @mixin last-child() { 234 | &:last-of-type { 235 | @content 236 | } 237 | } 238 | 239 | /// Select all even children. 240 | /// @group no-arguments 241 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 242 | @mixin even() { 243 | &:nth-child(even) { 244 | @content; 245 | } 246 | } 247 | 248 | /// Select all odd children. 249 | /// @group no-arguments 250 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 251 | @mixin odd() { 252 | &:nth-child(odd) { 253 | @content; 254 | } 255 | } 256 | 257 | /// Select only the first and last child. 258 | /// @group no-arguments 259 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 260 | @mixin first-last() { 261 | &:first-child, 262 | &:last-child { 263 | @content; 264 | } 265 | } 266 | 267 | /// Will only select the child if it’s unique. 268 | /// @group no-arguments 269 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 270 | /// @alias only 271 | @mixin unique() { 272 | &:only-child { 273 | @content; 274 | } 275 | } 276 | 277 | /// Will only select the child if it’s unique. 278 | /// @group no-arguments 279 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 280 | @mixin only() { 281 | &:only-child { 282 | @content; 283 | } 284 | } 285 | 286 | /// Will only select children if they are not unique. Meaning if there is at 287 | /// least 2 children, the style is applied. 288 | /// @group no-arguments 289 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 290 | @mixin not-unique() { 291 | &:not(:only-child) { 292 | @content; 293 | } 294 | } 295 | 296 | 297 | /// This mixin is used to automatically sort z-index in numerical order. But it 298 | /// can also sort them in anti-numerical order, depending the parameters you use. 299 | /// @group using functions 300 | /// @content [Write the style you want to apply to the children, and it will be added within the @content directive] 301 | /// @param {number} $num - Number of children 302 | /// @param {string} $direction [forward] - Direction of the sort 303 | /// @param {number} $index [0] - Index of the sorting 304 | @mixin child-index($num, $direction: 'forward', $index: 0) { 305 | @for $i from 1 through $num { 306 | @if ($direction == 'forward') { 307 | &:nth-child(#{$i}) { 308 | z-index: order-index($i, $index); 309 | @content; 310 | } 311 | } @else if ($direction == 'backward') { 312 | &:nth-last-child(#{$i}) { 313 | z-index: order-index($i, $index); 314 | @content; 315 | } 316 | } 317 | } 318 | } 319 | 320 | /// Used by the child-index mixin. It will returned the proper sorted numbers 321 | /// depending on the `$index` value. 322 | /// @access private 323 | /// @param {number} $num - Number of children 324 | /// @param {number} $index - Index of the sorting 325 | @function order-index($i, $index) { 326 | @return ($index + $i); 327 | } 328 | -------------------------------------------------------------------------------- /docs/src/styles/tools/_fonts.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Tools / Font Faces 3 | // ========================================================================== 4 | 5 | $global-font-file-formats: "woff2", "woff" !default; 6 | 7 | // 8 | // Builds the `src` list for an `@font-face` declaration. 9 | // 10 | // @link https://github.com/thoughtbot/bourbon/blob/master/core/bourbon/utilities/_font-source-declaration.scss 11 | // @link http://goo.gl/Ru1bKP 12 | // 13 | // @param {String} $font-family - The font family name. 14 | // @param {String} $file-path - The path to the font family. 15 | // @param {List} $file-formats - The file formats to request. 16 | // @return {List} 17 | // 18 | // @require {function} list-contains 19 | // 20 | // @access private 21 | // 22 | @function font-source-declaration( 23 | $font-family, 24 | $file-path, 25 | $file-formats 26 | ) { 27 | $src: (); 28 | $formats-map: ( 29 | eot: "#{$file-path}.eot?#iefix" format("embedded-opentype"), 30 | woff2: "#{$file-path}.woff2" format("woff2"), 31 | woff: "#{$file-path}.woff" format("woff"), 32 | ttf: "#{$file-path}.ttf" format("truetype"), 33 | svg: "#{$file-path}.svg##{$font-family}" format("svg"), 34 | ); 35 | 36 | @each $key, $values in $formats-map { 37 | @if list-contains($file-formats, $key) { 38 | $file-path: nth($values, 1); 39 | $font-format: nth($values, 2); 40 | $src: append($src, url("#{$assets-path}#{$file-path}") $font-format, comma); 41 | } 42 | } 43 | 44 | @return $src; 45 | } 46 | 47 | // 48 | // Generates an `@font-face` declaration. 49 | // 50 | // You can choose the specific file formats you need to output; the mixin supports 51 | // `eot`, `ttf`, `svg`, `woff2` and `woff`. 52 | // 53 | // @link https://github.com/thoughtbot/bourbon/blob/master/core/bourbon/library/_font-face.scss 54 | // 55 | // @param {String} $font-family - The font family name. 56 | // @param {String} $file-path - The path to the font family. 57 | // @param {String|List} $file-formats [("ttf", "woff2", "woff")] 58 | // A list of file formats to support, 59 | // for example ("eot", "ttf", "svg", "woff2", "woff"). 60 | // 61 | // @content 62 | // Any additional CSS properties that are included in the `@include` 63 | // directive will be output within the `@font-face` declaration, e.g. 64 | // you can pass in `font-weight`, `font-style` and/or `unicode-range`. 65 | // 66 | // @example scss 67 | // @include font-face( 68 | // "source-sans-pro", 69 | // "fonts/source-sans-pro-regular", 70 | // ("woff2", "woff") 71 | // ) { 72 | // font-style: normal; 73 | // font-weight: 400; 74 | // } 75 | // 76 | // // CSS Output 77 | // @font-face { 78 | // font-family: "source-sans-pro"; 79 | // src: url("fonts/source-sans-pro-regular.woff2") format("woff2"), 80 | // url("fonts/source-sans-pro-regular.woff") format("woff"); 81 | // font-style: normal; 82 | // font-weight: 400; 83 | // } 84 | // 85 | // @require {function} _font-source-declaration 86 | // @require {function} _retrieve-bourbon-setting 87 | // 88 | @mixin font-face( 89 | $font-family, 90 | $file-path, 91 | $file-formats: $global-font-file-formats 92 | ) { 93 | @font-face { 94 | font-family: $font-family; 95 | src: font-source-declaration( $font-family, $file-path, $file-formats); 96 | @content; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /docs/src/styles/tools/_functions.scss: -------------------------------------------------------------------------------- 1 | // Tools / Functions 2 | // ========================================================================== 3 | 4 | // 5 | // Converts the given pixel value to its EM quivalent. 6 | // 7 | // @param {Number} $size - The pixel value to convert. 8 | // @param {Number} $base [$font-size] - The assumed base font size. 9 | // @return {Number} Scalable pixel value in EMs. 10 | // 11 | @function em($size, $base: $font-size) { 12 | @if (type-of($size) == number) { 13 | @if (unit($size) != "px") { 14 | @error "`#{$size}` needs to be a pixel value."; 15 | } 16 | } @else { 17 | @error "`#{$size}` needs to be a number."; 18 | } 19 | 20 | @if (type-of($base) == number) { 21 | @if (unit($base) != "px") { 22 | @error "`#{$base}` needs to be a pixel value."; 23 | } 24 | } @else { 25 | @error "`#{$base}` needs to be a number."; 26 | } 27 | 28 | @return ($size / $base) * 1em; 29 | } 30 | 31 | // 32 | // Converts the given pixel value to its REM quivalent. 33 | // 34 | // @param {Number} $size - The pixel value to convert. 35 | // @param {Number} $base [$font-size] - The assumed base font size. 36 | // @return {Number} Scalable pixel value in REMs. 37 | // 38 | @function rem($size, $base: $font-size) { 39 | @if (type-of($size) == number) { 40 | @if (unit($size) != "px") { 41 | @error "`#{$size}` needs to be a pixel value."; 42 | } 43 | } @else { 44 | @error "`#{$size}` needs to be a number."; 45 | } 46 | 47 | @if (type-of($base) == number) { 48 | @if (unit($base) != "px") { 49 | @error "`#{$base}` needs to be a pixel value."; 50 | } 51 | } @else { 52 | @error "`#{$base}` needs to be a number."; 53 | } 54 | 55 | @return ($size / $base) * 1rem; 56 | } 57 | 58 | // 59 | // Converts a number to a percentage. 60 | // 61 | // @alias percentage() 62 | // @link http://sassdoc.com/annotations/#alias 63 | // @param {Number} $number - The value to convert. 64 | // @return {Number} A percentage. 65 | // 66 | @function span($number) { 67 | @return percentage($number); 68 | } 69 | 70 | // 71 | // Checks if a list contains a value(s). 72 | // 73 | // @link https://github.com/thoughtbot/bourbon/blob/master/core/bourbon/validators/_contains.scss 74 | // @param {List} $list - The list to check against. 75 | // @param {List} $values - A single value or list of values to check for. 76 | // @return {Boolean} 77 | // @access private 78 | // 79 | @function list-contains( 80 | $list, 81 | $values... 82 | ) { 83 | @each $value in $values { 84 | @if type-of(index($list, $value)) != "number" { 85 | @return false; 86 | } 87 | } 88 | 89 | @return true; 90 | } 91 | 92 | // 93 | // Resolve whether a rule is important or not. 94 | // 95 | // @param {Boolean} $flag - Whether a rule is important (TRUE) or not (FALSE). 96 | // @return {String|Null} Returns `!important` or NULL. 97 | // 98 | @function important($flag: false) { 99 | @if ($flag == true) { 100 | @return !important; 101 | } @elseif ($important == false) { 102 | @return null; 103 | } @else { 104 | @error "`#{$flag}` needs to be `true` or `false`." 105 | } 106 | } 107 | 108 | // 109 | // Determine if the current context is for a WYSIWYG editor. 110 | // 111 | // @requires {String} $context - The global context of the stylesheet. 112 | // @return {Boolean} If the $context is set to "editor". 113 | // 114 | @function is-editor() { 115 | @return ('editor' == $context); 116 | } 117 | 118 | // 119 | // Determine if the current context is for the front-end. 120 | // 121 | // @requires {String} $context - The global context of the stylesheet. 122 | // @return {Boolean} If the $context is set to "frontend". 123 | // 124 | @function is-frontend() { 125 | @return ('frontend' == $context); 126 | } 127 | 128 | $context: 'frontend' !default; 129 | -------------------------------------------------------------------------------- /docs/src/styles/tools/_layout.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Tools / Layout 3 | // ========================================================================== 4 | 5 | // 6 | // Grid-like layout system. 7 | // 8 | // The layout tools provide a column-style layout system. This file contains 9 | // the mixins to generate basic structural elements. 10 | // 11 | // @link https://github.com/inuitcss/inuitcss/blob/0420ba8/objects/_objects.layout.scss 12 | // 13 | 14 | // 15 | // Generate the layout container. 16 | // 17 | // 1. Use the negative margin trick for multi-row grids: 18 | // http://csswizardry.com/2011/08/building-better-grid-systems/ 19 | // 20 | // @requires {function} u-list-reset 21 | // @output `font-size`, `margin`, `padding`, `list-style` 22 | // 23 | @mixin o-layout($gutter: 0, $fix-whitespace: true) { 24 | margin: 0; 25 | padding: 0; 26 | list-style: none; 27 | 28 | @if ($fix-whitespace) { 29 | font-size: 0; 30 | } 31 | 32 | @if (type-of($gutter) == number) { 33 | margin-left: -$gutter; // [1] 34 | } 35 | } 36 | 37 | // 38 | // Generate the layout item. 39 | // 40 | // 1. Required in order to combine fluid widths with fixed gutters. 41 | // 2. Allows us to manipulate grids vertically, with text-level properties, 42 | // etc. 43 | // 3. Default item alignment is with the tops of each other, like most 44 | // traditional grid/layout systems. 45 | // 4. By default, all layout items are full-width (mobile first). 46 | // 5. Gutters provided by left padding: 47 | // http://csswizardry.com/2011/08/building-better-grid-systems/ 48 | // 49 | @mixin o-layout_item($gutter: 0, $fix-whitespace: true) { 50 | display: inline-block; // [2] 51 | width: 100%; // [4] 52 | vertical-align: top; // [3] 53 | 54 | @if ($fix-whitespace) { 55 | font-size: 1rem; 56 | } 57 | 58 | @if (type-of($gutter) == number) { 59 | padding-left: $gutter; // [5] 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /docs/src/styles/tools/_mixins.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Tools / Mixins 3 | // ========================================================================== 4 | 5 | // 6 | // Set the color of the highlight that appears over a link while it's being tapped. 7 | // 8 | // By default, the highlight is suppressed. 9 | // 10 | // @param {Color} $value [rgba(0, 0, 0, 0)] - The value of the highlight. 11 | // @output `-webkit-tap-highlight-color` 12 | // 13 | @mixin tap-highlight-color($value: rgba(0, 0, 0, 0)) { 14 | -webkit-tap-highlight-color: $value; 15 | } 16 | 17 | // 18 | // Set whether or not touch devices use momentum-based scrolling for the given element. 19 | // 20 | // By default, applies momentum-based scrolling for the current element. 21 | // 22 | // @param {String} $value [rgba(0, 0, 0, 0)] - The type of scrolling. 23 | // @output `-webkit-overflow-scrolling` 24 | // 25 | @mixin overflow-scrolling($value: touch) { 26 | -webkit-overflow-scrolling: $value; 27 | } 28 | 29 | // 30 | // Micro clearfix rules for containing floats. 31 | // 32 | // @link http://www.cssmojo.com/the-very-latest-clearfix-reloaded/ 33 | // @param {List} $supports The type of clearfix to generate. 34 | // @output Injects `:::after` pseudo-element. 35 | // 36 | @mixin u-clearfix($supports...) { 37 | &::after { 38 | display: if(list-contains($supports, table), table, block); 39 | clear: both; 40 | content: if(list-contains($supports, opera), " ", ""); 41 | } 42 | } 43 | 44 | // 45 | // Generate a font-size and baseline-compatible line-height. 46 | // 47 | // @link https://github.com/inuitcss/inuitcss/c14029c/tools/_tools.font-size.scss 48 | // @param {Number} $font-size - The size of the font. 49 | // @param {Number} $line-height [auto] - The line box height. 50 | // @param {Boolean} $important [false] - Whether the font-size is important. 51 | // @output `font-size`, `line-height` 52 | // 53 | @mixin font-size($font-size, $line-height: auto, $important: false) { 54 | $important: important($important); 55 | font-size: rem($font-size) $important; 56 | 57 | @if ($line-height == "auto") { 58 | line-height: ceil($font-size / $line-height) * ($line-height / $font-size) $important; 59 | } 60 | @else { 61 | @if (type-of($line-height) == number or $line-height == "inherit" or $line-height == "normal") { 62 | line-height: $line-height $important; 63 | } 64 | @elseif ($line-height != "none" and $line-height != false) { 65 | @error "D’oh! `#{$line-height}` is not a valid value for `$line-height`." 66 | } 67 | } 68 | } 69 | 70 | // 71 | // Vertically-center the direct descendants of the current element. 72 | // 73 | // Centering is achieved by displaying children as inline-blocks. Any whitespace 74 | // between elements is nullified by redefining the font size of the container 75 | // and its children. 76 | // 77 | // @output `font-size`, `display`, `vertical-align` 78 | // 79 | @mixin o-vertical-center { 80 | font-size: 0; 81 | 82 | &::before { 83 | display: inline-block; 84 | height: 100%; 85 | content: ""; 86 | vertical-align: middle; 87 | } 88 | 89 | > * { 90 | display: inline-block; 91 | vertical-align: middle; 92 | font-size: 1rem; 93 | } 94 | } 95 | 96 | // 97 | // Generate `:hover` and `:focus` styles in one go. 98 | // 99 | // @link https://github.com/inuitcss/inuitcss/blob/master/tools/_tools.mixins.scss 100 | // @content Wrapped in `:focus` and `:hover` pseudo-classes. 101 | // @output Wraps the given content in `:focus` and `:hover` pseudo-classes. 102 | // 103 | @mixin u-hocus { 104 | &:focus, 105 | &:hover { 106 | @content; 107 | } 108 | } 109 | 110 | // 111 | // Generate `:active` and `:focus` styles in one go. 112 | // 113 | // @see {Mixin} u-hocus 114 | // @content Wrapped in `:focus` and `:active` pseudo-classes. 115 | // @output Wraps the given content in `:focus` and `:hover` pseudo-classes. 116 | // 117 | @mixin u-actus { 118 | &:focus, 119 | &:active { 120 | @content; 121 | } 122 | } 123 | 124 | // 125 | // Prevent text from wrapping onto multiple lines for the current element. 126 | // 127 | // An ellipsis is appended to the end of the line. 128 | // 129 | // 1. Ensure that the node has a maximum width after which truncation can occur. 130 | // 2. Fix for IE 8/9 if `word-wrap: break-word` is in effect on ancestor nodes. 131 | // 132 | // @param {Number} $width [100%] - The maximum width of element. 133 | // @output `max-width`, `word-wrap`, `white-space`, `overflow`, `text-overflow` 134 | // 135 | @mixin u-truncate($width: 100%) { 136 | overflow: hidden; 137 | text-overflow: ellipsis; 138 | white-space: nowrap; 139 | word-wrap: normal; // [2] 140 | @if $width { 141 | max-width: $width; // [1] 142 | } 143 | } 144 | 145 | // 146 | // Applies accessible hiding to the current element. 147 | // 148 | // @param {Boolean} $important [true] - Whether the visibility is important. 149 | // @output Properties for removing the element from the document flow. 150 | // 151 | @mixin u-accessibly-hidden($important: true) { 152 | $important: important($important); 153 | position: absolute $important; 154 | overflow: hidden; 155 | clip: rect(0 0 0 0); 156 | margin: 0; 157 | padding: 0; 158 | width: 1px; 159 | height: 1px; 160 | border: 0; 161 | } 162 | 163 | // 164 | // Allows an accessibly hidden element to be focusable via keyboard navigation. 165 | // 166 | // @content For styling the now visible element. 167 | // @output Injects `:focus`, `:active` pseudo-classes. 168 | // 169 | @mixin u-accessibly-focusable { 170 | @include u-actus { 171 | clip: auto; 172 | width: auto; 173 | height: auto; 174 | 175 | @content; 176 | } 177 | } 178 | 179 | // 180 | // Hide the current element from all. 181 | // 182 | // The element will be hidden from screen readers and removed from the document flow. 183 | // 184 | // @link http://juicystudio.com/article/screen-readers-display-none.php 185 | // @param {Boolean} $important [true] - Whether the visibility is important. 186 | // @output `display`, `visibility` 187 | // 188 | @mixin u-hidden($important: true) { 189 | $important: important($important); 190 | display: none $important; 191 | visibility: hidden $important; 192 | } 193 | 194 | // 195 | // Show the current element for all. 196 | // 197 | // The element will be accessible from screen readers and visible in the document flow. 198 | // 199 | // @param {String} $display [block] - The rendering box used for the element. 200 | // @param {Boolean} $important [true] - Whether the visibility is important. 201 | // @output `display`, `visibility` 202 | // 203 | @mixin u-shown($display: block, $important: true) { 204 | $important: important($important); 205 | display: $display $important; 206 | visibility: visible $important; 207 | } 208 | 209 | // Customs 210 | 211 | @mixin u-label { 212 | font-size: rem(24px); 213 | text-transform: uppercase; 214 | font-weight: $semi-bold; 215 | } 216 | -------------------------------------------------------------------------------- /docs/src/styles/tools/_widths.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Tools / Widths 3 | // ========================================================================== 4 | 5 | // Optionally, the boilerplate can generate classes to offset items by a 6 | // certain width. Would you like to generate these types of class as well? E.g.: 7 | // 8 | // @example css 9 | // .u-push-1/3 10 | // .u-pull-2/4 11 | // .u-pull-1/5 12 | // .u-push-2/3 13 | $widths-offsets: false !default; 14 | 15 | // By default, the boilerplate uses fractions-like classes like `
`. 16 | // You can change the `/` to whatever you fancy with this variable. 17 | $fractions-delimiter: \/ !default; 18 | 19 | // When using Sass-MQ, this defines the separator for the breakpoints suffix 20 | // in the class name. By default, we are generating the responsive suffixes 21 | // for the classes with a `@` symbol so you get classes like: 22 | //
23 | $breakpoint-delimiter: \@ !default; 24 | 25 | // 26 | // Generate a series of width helper classes 27 | // 28 | // @example scss 29 | // @include widths(12); 30 | // 31 | // @example html 32 | //
33 | // 34 | // @example scss 35 | // @include widths(3 4, -mobile); 36 | // 37 | // @example html 38 | //
39 | // 40 | // @link https://github.com/inuitcss/inuitcss/commit/6eb574f/utilities/_utilities.widths.scss 41 | // @requires {Function} important 42 | // @requires {Function} $widths-offsets 43 | // @requires {Function} $fractions-delimiter 44 | // @requires {Function} $breakpoint-delimiter 45 | // @param {List} $colums - The columns we want the widths to have. 46 | // @param {String} $breakpoint - Optional suffix for responsive widths. 47 | // @output `width`, `position`, `right`, `left` 48 | // 49 | @mixin widths($columns, $breakpoint: null, $important: true) { 50 | $important: important($important); 51 | 52 | // Loop through the number of columns for each denominator of our fractions. 53 | @each $denominator in $columns { 54 | // Begin creating a numerator for our fraction up until we hit the 55 | // denominator. 56 | @for $numerator from 1 through $denominator { 57 | // Build a class in the format `.u-3/4[@]`. 58 | .u-#{$numerator}#{$fractions-delimiter}#{$denominator}#{$breakpoint} { 59 | width: ($numerator / $denominator) * 100% $important; 60 | } 61 | 62 | @if ($widths-offsets == true) { 63 | // Build a class in the format `.u-push-1/2[@]`. 64 | .u-push-#{$numerator}#{$fractions-delimiter}#{$denominator}#{$breakpoint} { 65 | position: relative $important; 66 | right: auto $important; 67 | left: ($numerator / $denominator) * 100% $important; 68 | } 69 | 70 | // Build a class in the format `.u-pull-5/6[@]`. 71 | .u-pull-#{$numerator}#{$fractions-delimiter}#{$denominator}#{$breakpoint} { 72 | position: relative $important; 73 | right: ($numerator / $denominator) * 100% $important; 74 | left: auto $important; 75 | } 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /docs/src/styles/utilities/_align.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Utilities / Alignment 3 | // ========================================================================== 4 | 5 | // Floats 6 | // ========================================================================== 7 | .u-float-left { 8 | float: left !important; 9 | } 10 | 11 | .u-float-right { 12 | float: right !important; 13 | } 14 | 15 | // Horizontal Text 16 | // ========================================================================== 17 | .u-text-center { 18 | text-align: center !important; 19 | } 20 | 21 | .u-text-left { 22 | text-align: left !important; 23 | } 24 | 25 | .u-text-right { 26 | text-align: right !important; 27 | } 28 | 29 | // Vertical Text 30 | // ========================================================================== 31 | .u-align-baseline { 32 | vertical-align: baseline !important; 33 | } 34 | 35 | .u-align-bottom { 36 | vertical-align: bottom !important; 37 | } 38 | 39 | .u-align-middle { 40 | vertical-align: middle !important; 41 | } 42 | 43 | .u-align-top { 44 | vertical-align: top !important; 45 | } 46 | 47 | .u-vertical-center { 48 | @include o-vertical-center; 49 | } 50 | -------------------------------------------------------------------------------- /docs/src/styles/utilities/_headings.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Utilities / Headings 3 | // ========================================================================== 4 | 5 | /** 6 | * Redefine all of our basic heading styles against utility classes so as to 7 | * provide larger (or smaller) generic font sizes. Anything more opinionated 8 | * than simple font-size changes should likely be applied via "o-" classes 9 | * 10 | * @example 11 | *

12 | * 13 | * @requires base/headings 14 | * @link http://csswizardry.com/2016/02/managing-typography-on-large-apps/ 15 | * @link https://github.com/inuitcss/inuitcss/blob/develop/utilities/_utilities.headings.scss 16 | */ 17 | 18 | .u-h1 { 19 | font-size: rem($font-size-h1) !important; 20 | } 21 | 22 | .u-h2 { 23 | font-size: rem($font-size-h2) !important; 24 | } 25 | 26 | .u-h3 { 27 | font-size: rem($font-size-h3) !important; 28 | } 29 | 30 | .u-h4 { 31 | font-size: rem($font-size-h4) !important; 32 | } 33 | 34 | .u-h5 { 35 | font-size: rem($font-size-h5) !important; 36 | } 37 | 38 | .u-h6 { 39 | font-size: rem($font-size-h6) !important; 40 | } 41 | -------------------------------------------------------------------------------- /docs/src/styles/utilities/_helpers.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Utilities / Helpers 3 | // ========================================================================== 4 | 5 | // Colors 6 | .u-white { 7 | color: $white; 8 | } 9 | 10 | // Text / font 11 | .u-label { 12 | @include u-label; 13 | } 14 | 15 | .u-icon { 16 | font-family: $font-lucida; 17 | font-size: rem(18px); 18 | } 19 | 20 | .u-text { 21 | font-size: rem(14px); 22 | } 23 | 24 | // Layout 25 | // ========================================================================== 26 | .u-clearfix { 27 | @include u-clearfix; 28 | } 29 | 30 | // Decorative 31 | // ============================================================================= 32 | .u-truncate { 33 | @include u-truncate; 34 | } 35 | 36 | // Visibility / Display 37 | // ========================================================================== 38 | [hidden][aria-hidden="false"] { 39 | position: absolute; 40 | display: inherit; 41 | clip: rect(0, 0, 0, 0); 42 | } 43 | 44 | [hidden][aria-hidden="false"]:focus { 45 | clip: auto; 46 | } 47 | 48 | // .u-block { 49 | // display: block; 50 | // } 51 | 52 | // /** 53 | // * 1. Fix for Firefox bug: an image styled `max-width:100%` within an 54 | // * inline-block will display at its default size, and not limit its width to 55 | // * 100% of an ancestral container. 56 | // */ 57 | // .u-inline-block { 58 | // display: inline-block !important; 59 | // max-width: 100%; /* 1 */ 60 | // } 61 | 62 | // .u-inline { 63 | // display: inline !important; 64 | // } 65 | 66 | // .u-table { 67 | // display: table !important; 68 | // } 69 | 70 | // .u-tableCell { 71 | // display: table-cell !important; 72 | // } 73 | 74 | // .u-tableRow { 75 | // display: table-row !important; 76 | // } 77 | 78 | /** 79 | * Completely remove from the flow but leave available to screen readers. 80 | */ 81 | .u-screen-reader-text { 82 | @include u-accessibly-hidden; 83 | } 84 | 85 | @media not print { 86 | .u-screen-reader-text\@screen { 87 | @include u-accessibly-hidden; 88 | } 89 | } 90 | 91 | /* 92 | * Extends the `.screen-reader-text` class to allow the element 93 | * to be focusable when navigated to via the keyboard. 94 | * 95 | * @link https://www.drupal.org/node/897638 96 | * @todo Define styles when focused. 97 | */ 98 | .u-screen-reader-text.-focusable { 99 | @include u-accessibly-focusable; 100 | } 101 | -------------------------------------------------------------------------------- /docs/src/styles/utilities/_print.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Utilities / Print Mode 3 | // ========================================================================== 4 | 5 | //// 6 | /// Very crude, reset-like styles taken from the HTML5 Boilerplate: 7 | /// - https://github.com/h5bp/html5-boilerplate/blob/5.3.0/dist/doc/css.md#print-styles 8 | /// - https://github.com/h5bp/html5-boilerplate/blob/master/dist/css/main.css#L205-L282 9 | /// 10 | /// @link https://github.com/inuitcss/inuitcss/blob/c27993f/utilities/_utilities.print.scss 11 | //// 12 | 13 | @media print { 14 | /** 15 | * 1. Black prints faster: http://www.sanbeiji.com/archives/953 16 | */ 17 | *, 18 | *:before, 19 | *:after, 20 | *:first-letter, 21 | *:first-line { 22 | background: transparent !important; 23 | box-shadow: none !important; 24 | color: #000000 !important; /* [1] */ 25 | text-shadow: none !important; 26 | } 27 | 28 | a, 29 | a:visited { 30 | text-decoration: underline; 31 | } 32 | 33 | a[href]:after { 34 | content: " (" attr(href) ")"; 35 | } 36 | 37 | abbr[title]:after { 38 | content: " (" attr(title) ")"; 39 | } 40 | 41 | /** 42 | * Don't show links that are fragment identifiers, or use the `javascript:` 43 | * pseudo protocol. 44 | */ 45 | a[href^="#"]:after, 46 | a[href^="javascript:"]:after { 47 | content: ""; 48 | } 49 | 50 | pre, 51 | blockquote { 52 | border: 1px solid #999999; 53 | page-break-inside: avoid; 54 | } 55 | 56 | /** 57 | * Printing Tables: http://css-discuss.incutio.com/wiki/Printing_Tables 58 | */ 59 | thead { 60 | display: table-header-group; 61 | } 62 | 63 | tr, 64 | img { 65 | page-break-inside: avoid; 66 | } 67 | 68 | 69 | img { 70 | max-width: 100% !important; 71 | } 72 | 73 | p, 74 | h2, 75 | h3 { 76 | orphans: 3; 77 | widows: 3; 78 | } 79 | 80 | h2, 81 | h3 { 82 | page-break-after: avoid; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /docs/src/styles/utilities/_ratio.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Utilities / Ratio 3 | // ========================================================================== 4 | 5 | // 6 | // @link https://github.com/inuitcss/inuitcss/blob/19d0c7e/objects/_objects.ratio.scss 7 | // 8 | 9 | // A list of aspect ratios that get generated as modifier classes. 10 | 11 | $aspect-ratios: ( 12 | (2:1), 13 | (4:3), 14 | (16:9), 15 | ) !default; 16 | 17 | /* stylelint-disable */ 18 | 19 | // 20 | // Generate a series of ratio classes to be used like so: 21 | // 22 | // @example 23 | //
24 | // 25 | // 26 | @each $ratio in $aspect-ratios { 27 | @each $antecedent, $consequent in $ratio { 28 | @if (type-of($antecedent) != number) { 29 | @error "`#{$antecedent}` needs to be a number." 30 | } 31 | 32 | @if (type-of($consequent) != number) { 33 | @error "`#{$consequent}` needs to be a number." 34 | } 35 | 36 | &.u-#{$antecedent}\:#{$consequent}::before { 37 | padding-bottom: ($consequent/$antecedent) * 100%; 38 | } 39 | } 40 | } 41 | 42 | /* stylelint-enable */ 43 | -------------------------------------------------------------------------------- /docs/src/styles/utilities/_spacing.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Utilities / Spacing 3 | // ========================================================================== 4 | 5 | //// 6 | /// Utility classes to put specific spacing values onto elements. The below loop 7 | /// will generate us a suite of classes like: 8 | /// 9 | /// @example 10 | /// .u-margin-top {} 11 | /// .u-padding-left-large {} 12 | /// .u-margin-right-small {} 13 | /// .u-padding {} 14 | /// .u-padding-right-none {} 15 | /// .u-padding-horizontal {} 16 | /// .u-padding-vertical-small {} 17 | /// 18 | /// @link https://github.com/inuitcss/inuitcss/blob/512977a/utilities/_utilities.spacing.scss 19 | //// 20 | 21 | /* stylelint-disable string-quotes */ 22 | 23 | $spacing-directions: ( 24 | null: null, 25 | '-top': '-top', 26 | '-right': '-right', 27 | '-bottom': '-bottom', 28 | '-left': '-left', 29 | '-horizontal': '-left' '-right', 30 | '-vertical': '-top' '-bottom', 31 | ) !default; 32 | 33 | $spacing-properties: ( 34 | 'padding': 'padding', 35 | 'margin': 'margin', 36 | ) !default; 37 | 38 | $spacing-sizes: ( 39 | null: $unit, 40 | '-small': $unit-small, 41 | '-none': 0 42 | ) !default; 43 | 44 | @each $property-namespace, $property in $spacing-properties { 45 | @each $direction-namespace, $direction-rules in $spacing-directions { 46 | @each $size-namespace, $size in $spacing-sizes { 47 | .u-#{$property-namespace}#{$direction-namespace}#{$size-namespace} { 48 | @each $direction in $direction-rules { 49 | #{$property}#{$direction}: $size !important; 50 | } 51 | } 52 | } 53 | } 54 | } 55 | 56 | /* stylelint-enable string-quotes */ 57 | -------------------------------------------------------------------------------- /docs/src/styles/utilities/_states.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Utilities / States 3 | // ========================================================================== 4 | 5 | /** 6 | * ARIA roles display visual cursor hints 7 | */ 8 | [aria-busy="true"] { 9 | cursor: progress; 10 | } 11 | 12 | [aria-controls] { 13 | cursor: pointer; 14 | } 15 | 16 | [aria-disabled] { 17 | cursor: default; 18 | } 19 | 20 | /** 21 | * Control visibility without affecting flow. 22 | */ 23 | 24 | .is-visible { 25 | visibility: visible !important; 26 | opacity: 1 !important; 27 | } 28 | 29 | .is-invisible { 30 | visibility: hidden !important; 31 | opacity: 0 !important; 32 | } 33 | 34 | /** 35 | * Completely remove from the flow and screen readers. 36 | */ 37 | 38 | .is-hidden { 39 | @include u-hidden; 40 | } 41 | 42 | @media not print { 43 | .is-hidden\@screen { 44 | @include u-hidden; 45 | } 46 | } 47 | 48 | @media print { 49 | .is-hidden\@print { 50 | @include u-hidden; 51 | } 52 | } 53 | 54 | // .is-hidden\@to-large { 55 | // @media (max-width: $to-large) { 56 | // display: none; 57 | // } 58 | // } 59 | 60 | // .is-hidden\@from-large { 61 | // @media (min-width: $from-large) { 62 | // display: none; 63 | // } 64 | // } 65 | 66 | // /** 67 | // * Display a hidden-by-default element. 68 | // */ 69 | 70 | // .is-shown { 71 | // @include u-shown; 72 | // } 73 | 74 | // table.is-shown { 75 | // display: table !important; 76 | // } 77 | 78 | // tr.is-shown { 79 | // display: table-row !important; 80 | // } 81 | 82 | // td.is-shown, 83 | // th.is-shown { 84 | // display: table-cell !important; 85 | // } 86 | -------------------------------------------------------------------------------- /docs/src/styles/utilities/_widths.scss: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // Utilities / Widths 3 | // ========================================================================== 4 | 5 | //// 6 | /// @link https://github.com/inuitcss/inuitcss/blob/6eb574f/utilities/_utilities.widths.scss 7 | //// 8 | 9 | /// Which fractions would you like in your grid system(s)? 10 | /// By default, the boilerplate provides fractions of one whole, halves, thirds, 11 | /// quarters, and fifths, e.g.: 12 | /// 13 | /// @example css 14 | /// .u-1/2 15 | /// .u-2/5 16 | /// .u-3/4 17 | /// .u-2/3 18 | $widths-fractions: 1 2 3 4 5 !default; 19 | 20 | @include widths($widths-fractions); 21 | 22 | .u-1\/2\@from-small { 23 | @media (min-width: $from-small) { 24 | width: span(1/2); 25 | } 26 | } 27 | 28 | .u-1\/3\@from-medium { 29 | @media (min-width: $from-medium) { 30 | width: span(1/3); 31 | } 32 | } 33 | 34 | .u-1\/2\@from-medium { 35 | @media (min-width: $from-medium) { 36 | width: span(1/2); 37 | } 38 | } 39 | 40 | .u-2\/5\@from-medium { 41 | @media (min-width: $from-medium) { 42 | width: span(2/5); 43 | } 44 | } 45 | 46 | .u-3\/5\@from-medium { 47 | @media (min-width: $from-medium) { 48 | width: span(3/5); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /docs/src/styles/vendors/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/locomotivemtl/locomotive-scroll/8ac3990434182eb3e782edeae3d16e1cdf3ac015/docs/src/styles/vendors/.gitkeep -------------------------------------------------------------------------------- /gulpfile.babel.js: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import styles from './build/styles.js'; 3 | import scripts from './build/scripts.js'; 4 | import svgs from './build/svgs.js'; 5 | import serve from './build/serve.js'; 6 | import watch from './build/watch.js'; 7 | import copy from './build/copy.js'; 8 | import { buildStyles, buildScripts } from './build/build.js'; 9 | 10 | const compile = gulp.series(styles, scripts, svgs); 11 | const main = gulp.series(copy, compile, serve, watch); 12 | const build = gulp.series(copy, compile, buildStyles, buildScripts); 13 | 14 | gulp.task('default', main); 15 | gulp.task('compile', compile); 16 | gulp.task('build', build); 17 | gulp.task('copy', copy); 18 | -------------------------------------------------------------------------------- /mconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "src": "./src/", 3 | "dest": "./dist/", 4 | "docs": { 5 | "src": "./docs/src/", 6 | "dest": "./docs/" 7 | }, 8 | "build": "./build/", 9 | "styles": { 10 | "src": "./src/", 11 | "dest": "./dist/", 12 | "main": "locomotive-scroll", 13 | "docs": { 14 | "src": "./docs/src/styles/", 15 | "dest": "./docs/dist/styles/", 16 | "main": "main" 17 | } 18 | }, 19 | "scripts": { 20 | "src": "./src/", 21 | "dest": "./dist/", 22 | "main": "locomotive-scroll", 23 | "docs": { 24 | "src": "./docs/src/scripts/", 25 | "dest": "./docs/dist/scripts/", 26 | "main": "main" 27 | } 28 | }, 29 | "svgs": { 30 | "src": "./docs/src/images/sprite/", 31 | "dest": "./docs/dist/images/" 32 | }, 33 | "views": { 34 | "src": "./docs/" 35 | }, 36 | "modules": { 37 | "build": "gulp", 38 | "style": "sass", 39 | "script": "js", 40 | "view": false 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "locomotive-scroll", 3 | "version": "4.1.4", 4 | "description": "Detection of elements in viewport & smooth scrolling with parallax effects.", 5 | "repository": "locomotivemtl/locomotive-scroll", 6 | "author": "Locomotive (https://locomotive.ca)", 7 | "license": "MIT", 8 | "main": "./dist/locomotive-scroll.js", 9 | "module": "./dist/locomotive-scroll.esm.js", 10 | "scripts": { 11 | "start": "gulp", 12 | "format": "prettier --write 'src/**/*.js'", 13 | "build": "npm run format && gulp build" 14 | }, 15 | "devDependencies": { 16 | "@modularbp/gulp": "^1.0.5", 17 | "@modularbp/gulp-build": "^1.1.0", 18 | "@modularbp/gulp-error": "^1.0.3", 19 | "@modularbp/gulp-js": "^1.0.8", 20 | "@modularbp/gulp-notify": "^1.0.3", 21 | "@modularbp/gulp-sass": "^1.0.7", 22 | "@modularbp/gulp-serve": "^1.0.5", 23 | "@modularbp/gulp-svg": "^1.0.5", 24 | "@modularbp/gulp-watch": "^1.0.5", 25 | "gulp-header": "^2.0.9", 26 | "mbp": "^1.3.0", 27 | "merge-stream": "^2.0.0", 28 | "node-sass": "^6.0.1", 29 | "normalize.css": "^8.0.1", 30 | "postcss": "^8.3.11", 31 | "prettier": "^2.1.2" 32 | }, 33 | "dependencies": { 34 | "bezier-easing": "^2.1.0", 35 | "smoothscroll-polyfill": "^0.4.4", 36 | "virtual-scroll": "^1.5.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/locomotive-scroll.js: -------------------------------------------------------------------------------- 1 | export { default } from './scripts/Main'; 2 | export { Native } from './scripts/NativeMain'; 3 | export { Smooth } from './scripts/Main'; 4 | -------------------------------------------------------------------------------- /src/locomotive-scroll.scss: -------------------------------------------------------------------------------- 1 | @import "styles/base"; 2 | @import "styles/scrollbar"; 3 | -------------------------------------------------------------------------------- /src/locomotive-scroll.umd.js: -------------------------------------------------------------------------------- 1 | export { default } from './scripts/Main'; 2 | -------------------------------------------------------------------------------- /src/scripts/Core.js: -------------------------------------------------------------------------------- 1 | import { defaults } from './options'; 2 | 3 | export default class { 4 | constructor(options = {}) { 5 | Object.assign(this, defaults, options); 6 | this.smartphone = defaults.smartphone; 7 | if (options.smartphone) Object.assign(this.smartphone, options.smartphone); 8 | this.tablet = defaults.tablet; 9 | if (options.tablet) Object.assign(this.tablet, options.tablet); 10 | 11 | this.namespace = 'locomotive'; 12 | this.html = document.documentElement; 13 | this.windowHeight = window.innerHeight; 14 | this.windowWidth = window.innerWidth; 15 | this.windowMiddle = { 16 | x: this.windowWidth / 2, 17 | y: this.windowHeight / 2 18 | }; 19 | this.els = {}; 20 | this.currentElements = {}; 21 | this.listeners = {}; 22 | 23 | this.hasScrollTicking = false; 24 | this.hasCallEventSet = false; 25 | 26 | this.checkScroll = this.checkScroll.bind(this); 27 | this.checkResize = this.checkResize.bind(this); 28 | this.checkEvent = this.checkEvent.bind(this); 29 | 30 | this.instance = { 31 | scroll: { 32 | x: 0, 33 | y: 0 34 | }, 35 | limit: { 36 | x: this.html.offsetWidth, 37 | y: this.html.offsetHeight 38 | }, 39 | currentElements: this.currentElements 40 | }; 41 | 42 | if (this.isMobile) { 43 | if (this.isTablet) { 44 | this.context = 'tablet'; 45 | } else { 46 | this.context = 'smartphone'; 47 | } 48 | } else { 49 | this.context = 'desktop'; 50 | } 51 | 52 | if (this.isMobile) this.direction = this[this.context].direction; 53 | if (this.direction === 'horizontal') { 54 | this.directionAxis = 'x'; 55 | } else { 56 | this.directionAxis = 'y'; 57 | } 58 | 59 | if (this.getDirection) { 60 | this.instance.direction = null; 61 | } 62 | 63 | if (this.getDirection) { 64 | this.instance.speed = 0; 65 | } 66 | 67 | this.html.classList.add(this.initClass); 68 | 69 | window.addEventListener('resize', this.checkResize, false); 70 | } 71 | 72 | init() { 73 | this.initEvents(); 74 | } 75 | 76 | checkScroll() { 77 | this.dispatchScroll(); 78 | } 79 | 80 | checkResize() { 81 | if (!this.resizeTick) { 82 | this.resizeTick = true; 83 | requestAnimationFrame(() => { 84 | this.resize(); 85 | this.resizeTick = false; 86 | }); 87 | } 88 | } 89 | 90 | resize() {} 91 | 92 | checkContext() { 93 | if (!this.reloadOnContextChange) return; 94 | 95 | this.isMobile = 96 | /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || 97 | (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) || 98 | this.windowWidth < this.tablet.breakpoint; 99 | this.isTablet = this.isMobile && this.windowWidth >= this.tablet.breakpoint; 100 | 101 | let oldContext = this.context; 102 | if (this.isMobile) { 103 | if (this.isTablet) { 104 | this.context = 'tablet'; 105 | } else { 106 | this.context = 'smartphone'; 107 | } 108 | } else { 109 | this.context = 'desktop'; 110 | } 111 | 112 | if (oldContext != this.context) { 113 | let oldSmooth = oldContext == 'desktop' ? this.smooth : this[oldContext].smooth; 114 | let newSmooth = this.context == 'desktop' ? this.smooth : this[this.context].smooth; 115 | 116 | if (oldSmooth != newSmooth) window.location.reload(); 117 | } 118 | } 119 | 120 | initEvents() { 121 | this.scrollToEls = this.el.querySelectorAll(`[data-${this.name}-to]`); 122 | this.setScrollTo = this.setScrollTo.bind(this); 123 | 124 | this.scrollToEls.forEach((el) => { 125 | el.addEventListener('click', this.setScrollTo, false); 126 | }); 127 | } 128 | 129 | setScrollTo(event) { 130 | event.preventDefault(); 131 | 132 | this.scrollTo( 133 | event.currentTarget.getAttribute(`data-${this.name}-href`) || 134 | event.currentTarget.getAttribute('href'), 135 | { 136 | offset: event.currentTarget.getAttribute(`data-${this.name}-offset`) 137 | } 138 | ); 139 | } 140 | 141 | addElements() {} 142 | 143 | detectElements(hasCallEventSet) { 144 | const scrollTop = this.instance.scroll.y; 145 | const scrollBottom = scrollTop + this.windowHeight; 146 | 147 | const scrollLeft = this.instance.scroll.x; 148 | const scrollRight = scrollLeft + this.windowWidth; 149 | 150 | Object.entries(this.els).forEach(([i, el]) => { 151 | if (el && (!el.inView || hasCallEventSet)) { 152 | if (this.direction === 'horizontal') { 153 | if (scrollRight >= el.left && scrollLeft < el.right) { 154 | this.setInView(el, i); 155 | } 156 | } else { 157 | if (scrollBottom >= el.top && scrollTop < el.bottom) { 158 | this.setInView(el, i); 159 | } 160 | } 161 | } 162 | 163 | if (el && el.inView) { 164 | if (this.direction === 'horizontal') { 165 | let width = el.right - el.left; 166 | el.progress = 167 | (this.instance.scroll.x - (el.left - this.windowWidth)) / 168 | (width + this.windowWidth); 169 | 170 | if (scrollRight < el.left || scrollLeft > el.right) { 171 | this.setOutOfView(el, i); 172 | } 173 | } else { 174 | let height = el.bottom - el.top; 175 | el.progress = 176 | (this.instance.scroll.y - (el.top - this.windowHeight)) / 177 | (height + this.windowHeight); 178 | 179 | if (scrollBottom < el.top || scrollTop > el.bottom) { 180 | this.setOutOfView(el, i); 181 | } 182 | } 183 | } 184 | }); 185 | 186 | // this.els = this.els.filter((current, i) => { 187 | // return current !== null; 188 | // }); 189 | 190 | this.hasScrollTicking = false; 191 | } 192 | 193 | setInView(current, i) { 194 | this.els[i].inView = true; 195 | current.el.classList.add(current.class); 196 | 197 | this.currentElements[i] = current; 198 | 199 | if (current.call && this.hasCallEventSet) { 200 | this.dispatchCall(current, 'enter'); 201 | 202 | if (!current.repeat) { 203 | this.els[i].call = false; 204 | } 205 | } 206 | 207 | // if (!current.repeat && !current.speed && !current.sticky) { 208 | // if (!current.call || current.call && this.hasCallEventSet) { 209 | // this.els[i] = null 210 | // } 211 | // } 212 | } 213 | 214 | setOutOfView(current, i) { 215 | // if (current.repeat || current.speed !== undefined) { 216 | this.els[i].inView = false; 217 | // } 218 | 219 | Object.keys(this.currentElements).forEach((el) => { 220 | el === i && delete this.currentElements[el]; 221 | }); 222 | 223 | if (current.call && this.hasCallEventSet) { 224 | this.dispatchCall(current, 'exit'); 225 | } 226 | 227 | if (current.repeat) { 228 | current.el.classList.remove(current.class); 229 | } 230 | } 231 | 232 | dispatchCall(current, way) { 233 | this.callWay = way; 234 | this.callValue = current.call.split(',').map((item) => item.trim()); 235 | this.callObj = current; 236 | 237 | if (this.callValue.length == 1) this.callValue = this.callValue[0]; 238 | 239 | const callEvent = new Event(this.namespace + 'call'); 240 | this.el.dispatchEvent(callEvent); 241 | } 242 | 243 | dispatchScroll() { 244 | const scrollEvent = new Event(this.namespace + 'scroll'); 245 | this.el.dispatchEvent(scrollEvent); 246 | } 247 | 248 | setEvents(event, func) { 249 | if (!this.listeners[event]) { 250 | this.listeners[event] = []; 251 | } 252 | 253 | const list = this.listeners[event]; 254 | list.push(func); 255 | 256 | if (list.length === 1) { 257 | this.el.addEventListener(this.namespace + event, this.checkEvent, false); 258 | } 259 | 260 | if (event === 'call') { 261 | this.hasCallEventSet = true; 262 | this.detectElements(true); 263 | } 264 | } 265 | 266 | unsetEvents(event, func) { 267 | if (!this.listeners[event]) return; 268 | 269 | const list = this.listeners[event]; 270 | const index = list.indexOf(func); 271 | 272 | if (index < 0) return; 273 | 274 | list.splice(index, 1); 275 | 276 | if (list.index === 0) { 277 | this.el.removeEventListener(this.namespace + event, this.checkEvent, false); 278 | } 279 | } 280 | 281 | checkEvent(event) { 282 | const name = event.type.replace(this.namespace, ''); 283 | const list = this.listeners[name]; 284 | 285 | if (!list || list.length === 0) return; 286 | 287 | list.forEach((func) => { 288 | switch (name) { 289 | case 'scroll': 290 | return func(this.instance); 291 | case 'call': 292 | return func(this.callValue, this.callWay, this.callObj); 293 | default: 294 | return func(); 295 | } 296 | }); 297 | } 298 | 299 | startScroll() {} 300 | 301 | stopScroll() {} 302 | 303 | setScroll(x, y) { 304 | this.instance.scroll = { 305 | x: 0, 306 | y: 0 307 | }; 308 | } 309 | 310 | destroy() { 311 | window.removeEventListener('resize', this.checkResize, false); 312 | 313 | Object.keys(this.listeners).forEach((event) => { 314 | this.el.removeEventListener(this.namespace + event, this.checkEvent, false); 315 | }); 316 | this.listeners = {}; 317 | 318 | this.scrollToEls.forEach((el) => { 319 | el.removeEventListener('click', this.setScrollTo, false); 320 | }); 321 | 322 | this.html.classList.remove(this.initClass); 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /src/scripts/Main.js: -------------------------------------------------------------------------------- 1 | import { defaults } from './options'; 2 | import NativeScroll from './Native'; 3 | import SmoothScroll from './Smooth'; 4 | 5 | export class Smooth { 6 | constructor(options = {}) { 7 | this.options = options; 8 | 9 | // Override default options with given ones 10 | Object.assign(this, defaults, options); 11 | this.smartphone = defaults.smartphone; 12 | if (options.smartphone) Object.assign(this.smartphone, options.smartphone); 13 | this.tablet = defaults.tablet; 14 | if (options.tablet) Object.assign(this.tablet, options.tablet); 15 | 16 | if (!this.smooth && this.direction == 'horizontal') 17 | console.warn('🚨 `smooth:false` & `horizontal` direction are not yet compatible'); 18 | if (!this.tablet.smooth && this.tablet.direction == 'horizontal') 19 | console.warn( 20 | '🚨 `smooth:false` & `horizontal` direction are not yet compatible (tablet)' 21 | ); 22 | if (!this.smartphone.smooth && this.smartphone.direction == 'horizontal') 23 | console.warn( 24 | '🚨 `smooth:false` & `horizontal` direction are not yet compatible (smartphone)' 25 | ); 26 | 27 | this.init(); 28 | } 29 | 30 | init() { 31 | this.options.isMobile = 32 | /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || 33 | (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) || 34 | window.innerWidth < this.tablet.breakpoint; 35 | this.options.isTablet = 36 | this.options.isMobile && window.innerWidth >= this.tablet.breakpoint; 37 | 38 | if ( 39 | (this.smooth && !this.options.isMobile) || 40 | (this.tablet.smooth && this.options.isTablet) || 41 | (this.smartphone.smooth && this.options.isMobile && !this.options.isTablet) 42 | ) { 43 | this.scroll = new SmoothScroll(this.options); 44 | } else { 45 | this.scroll = new NativeScroll(this.options); 46 | } 47 | 48 | this.scroll.init(); 49 | 50 | if (window.location.hash) { 51 | // Get the hash without the '#' and find the matching element 52 | const id = window.location.hash.slice(1, window.location.hash.length); 53 | let target = document.getElementById(id); 54 | 55 | // If found, scroll to the element 56 | if (target) this.scroll.scrollTo(target); 57 | } 58 | } 59 | 60 | update() { 61 | this.scroll.update(); 62 | } 63 | 64 | start() { 65 | this.scroll.startScroll(); 66 | } 67 | 68 | stop() { 69 | this.scroll.stopScroll(); 70 | } 71 | 72 | scrollTo(target, options) { 73 | this.scroll.scrollTo(target, options); 74 | } 75 | 76 | setScroll(x, y) { 77 | this.scroll.setScroll(x, y); 78 | } 79 | 80 | on(event, func) { 81 | this.scroll.setEvents(event, func); 82 | } 83 | 84 | off(event, func) { 85 | this.scroll.unsetEvents(event, func); 86 | } 87 | 88 | destroy() { 89 | this.scroll.destroy(); 90 | } 91 | } 92 | 93 | export default Smooth; 94 | -------------------------------------------------------------------------------- /src/scripts/Native.js: -------------------------------------------------------------------------------- 1 | import Core from './Core'; 2 | import smoothscroll from 'smoothscroll-polyfill'; 3 | 4 | export default class extends Core { 5 | constructor(options = {}) { 6 | super(options); 7 | 8 | if (this.resetNativeScroll) { 9 | if (history.scrollRestoration) { 10 | history.scrollRestoration = 'manual'; 11 | } 12 | window.scrollTo(0, 0); 13 | } 14 | 15 | window.addEventListener('scroll', this.checkScroll, false); 16 | 17 | if (window.smoothscrollPolyfill === undefined) { 18 | window.smoothscrollPolyfill = smoothscroll; 19 | window.smoothscrollPolyfill.polyfill(); 20 | } 21 | } 22 | 23 | init() { 24 | this.instance.scroll.y = window.pageYOffset; 25 | 26 | this.addElements(); 27 | this.detectElements(); 28 | 29 | super.init(); 30 | } 31 | 32 | checkScroll() { 33 | super.checkScroll(); 34 | 35 | if (this.getDirection) { 36 | this.addDirection(); 37 | } 38 | 39 | if (this.getSpeed) { 40 | this.addSpeed(); 41 | this.speedTs = Date.now(); 42 | } 43 | 44 | this.instance.scroll.y = window.pageYOffset; 45 | 46 | if (Object.entries(this.els).length) { 47 | if (!this.hasScrollTicking) { 48 | requestAnimationFrame(() => { 49 | this.detectElements(); 50 | }); 51 | this.hasScrollTicking = true; 52 | } 53 | } 54 | } 55 | 56 | addDirection() { 57 | if (window.pageYOffset > this.instance.scroll.y) { 58 | if (this.instance.direction !== 'down') { 59 | this.instance.direction = 'down'; 60 | } 61 | } else if (window.pageYOffset < this.instance.scroll.y) { 62 | if (this.instance.direction !== 'up') { 63 | this.instance.direction = 'up'; 64 | } 65 | } 66 | } 67 | 68 | addSpeed() { 69 | if (window.pageYOffset != this.instance.scroll.y) { 70 | this.instance.speed = 71 | (window.pageYOffset - this.instance.scroll.y) / 72 | Math.max(1, Date.now() - this.speedTs); 73 | } else { 74 | this.instance.speed = 0; 75 | } 76 | } 77 | 78 | resize() { 79 | if (Object.entries(this.els).length) { 80 | this.windowHeight = window.innerHeight; 81 | this.updateElements(); 82 | } 83 | } 84 | 85 | addElements() { 86 | this.els = {}; 87 | const els = this.el.querySelectorAll('[data-' + this.name + ']'); 88 | 89 | els.forEach((el, index) => { 90 | const BCR = el.getBoundingClientRect(); 91 | let cl = el.dataset[this.name + 'Class'] || this.class; 92 | let id = 93 | typeof el.dataset[this.name + 'Id'] === 'string' 94 | ? el.dataset[this.name + 'Id'] 95 | : index; 96 | let top; 97 | let left; 98 | let offset = 99 | typeof el.dataset[this.name + 'Offset'] === 'string' 100 | ? el.dataset[this.name + 'Offset'].split(',') 101 | : this.offset; 102 | let repeat = el.dataset[this.name + 'Repeat']; 103 | let call = el.dataset[this.name + 'Call']; 104 | 105 | let target = el.dataset[this.name + 'Target']; 106 | let targetEl; 107 | 108 | if (target !== undefined) { 109 | targetEl = document.querySelector(`${target}`); 110 | } else { 111 | targetEl = el; 112 | } 113 | 114 | const targetElBCR = targetEl.getBoundingClientRect(); 115 | top = targetElBCR.top + this.instance.scroll.y; 116 | left = targetElBCR.left + this.instance.scroll.x; 117 | 118 | let bottom = top + targetEl.offsetHeight; 119 | let right = left + targetEl.offsetWidth; 120 | 121 | if (repeat == 'false') { 122 | repeat = false; 123 | } else if (repeat != undefined) { 124 | repeat = true; 125 | } else { 126 | repeat = this.repeat; 127 | } 128 | 129 | let relativeOffset = this.getRelativeOffset(offset); 130 | top = top + relativeOffset[0]; 131 | bottom = bottom - relativeOffset[1]; 132 | 133 | const mappedEl = { 134 | el: el, 135 | targetEl: targetEl, 136 | id, 137 | class: cl, 138 | top: top, 139 | bottom: bottom, 140 | left, 141 | right, 142 | offset, 143 | progress: 0, 144 | repeat, 145 | inView: false, 146 | call 147 | }; 148 | 149 | this.els[id] = mappedEl; 150 | if (el.classList.contains(cl)) { 151 | this.setInView(this.els[id], id); 152 | } 153 | }); 154 | } 155 | 156 | updateElements() { 157 | Object.entries(this.els).forEach(([i, el]) => { 158 | const top = el.targetEl.getBoundingClientRect().top + this.instance.scroll.y; 159 | const bottom = top + el.targetEl.offsetHeight; 160 | const relativeOffset = this.getRelativeOffset(el.offset); 161 | 162 | this.els[i].top = top + relativeOffset[0]; 163 | this.els[i].bottom = bottom - relativeOffset[1]; 164 | }); 165 | 166 | this.hasScrollTicking = false; 167 | } 168 | 169 | getRelativeOffset(offset) { 170 | let relativeOffset = [0, 0]; 171 | 172 | if (offset) { 173 | for (var i = 0; i < offset.length; i++) { 174 | if (typeof offset[i] == 'string') { 175 | if (offset[i].includes('%')) { 176 | relativeOffset[i] = parseInt( 177 | (offset[i].replace('%', '') * this.windowHeight) / 100 178 | ); 179 | } else { 180 | relativeOffset[i] = parseInt(offset[i]); 181 | } 182 | } else { 183 | relativeOffset[i] = offset[i]; 184 | } 185 | } 186 | } 187 | 188 | return relativeOffset; 189 | } 190 | 191 | /** 192 | * Scroll to a desired target. 193 | * 194 | * @param Available options : 195 | * target {node, string, "top", "bottom", int} - The DOM element we want to scroll to 196 | * options {object} - Options object for additional settings. 197 | * @return {void} 198 | */ 199 | scrollTo(target, options = {}) { 200 | // Parse options 201 | let offset = parseInt(options.offset) || 0; // An offset to apply on top of given `target` or `sourceElem`'s target 202 | const callback = options.callback ? options.callback : false; // function called when scrollTo completes (note that it won't wait for lerp to stabilize) 203 | 204 | if (typeof target === 'string') { 205 | // Selector or boundaries 206 | if (target === 'top') { 207 | target = this.html; 208 | } else if (target === 'bottom') { 209 | target = this.html.offsetHeight - window.innerHeight; 210 | } else { 211 | target = document.querySelector(target); 212 | // If the query fails, abort 213 | if (!target) { 214 | return; 215 | } 216 | } 217 | } else if (typeof target === 'number') { 218 | // Absolute coordinate 219 | target = parseInt(target); 220 | } else if (target && target.tagName) { 221 | // DOM Element 222 | // We good 👍 223 | } else { 224 | console.warn('`target` parameter is not valid'); 225 | return; 226 | } 227 | 228 | // We have a target that is not a coordinate yet, get it 229 | if (typeof target !== 'number') { 230 | offset = target.getBoundingClientRect().top + offset + this.instance.scroll.y; 231 | } else { 232 | offset = target + offset; 233 | } 234 | 235 | const isTargetReached = () => { 236 | return parseInt(window.pageYOffset) === parseInt(offset); 237 | }; 238 | if (callback) { 239 | if (isTargetReached()) { 240 | callback(); 241 | return; 242 | } else { 243 | let onScroll = function () { 244 | if (isTargetReached()) { 245 | window.removeEventListener('scroll', onScroll); 246 | callback(); 247 | } 248 | }; 249 | window.addEventListener('scroll', onScroll); 250 | } 251 | } 252 | 253 | window.scrollTo({ 254 | top: offset, 255 | behavior: options.duration === 0 ? 'auto' : 'smooth' 256 | }); 257 | } 258 | 259 | update() { 260 | this.addElements(); 261 | this.detectElements(); 262 | } 263 | 264 | destroy() { 265 | super.destroy(); 266 | 267 | window.removeEventListener('scroll', this.checkScroll, false); 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/scripts/NativeMain.js: -------------------------------------------------------------------------------- 1 | import { defaults } from './options'; 2 | import Scroll from './Native'; 3 | 4 | export class Native { 5 | constructor(options = {}) { 6 | this.options = options; 7 | 8 | // Override default options with given ones 9 | Object.assign(this, defaults, options); 10 | this.smartphone = defaults.smartphone; 11 | if (options.smartphone) Object.assign(this.smartphone, options.smartphone); 12 | this.tablet = defaults.tablet; 13 | if (options.tablet) Object.assign(this.tablet, options.tablet); 14 | 15 | this.init(); 16 | } 17 | 18 | init() { 19 | this.scroll = new Scroll(this.options); 20 | 21 | this.scroll.init(); 22 | 23 | if (window.location.hash) { 24 | // Get the hash without the '#' and find the matching element 25 | const id = window.location.hash.slice(1, window.location.hash.length); 26 | let target = document.getElementById(id); 27 | 28 | // If found, scroll to the element 29 | if (target) this.scroll.scrollTo(target); 30 | } 31 | } 32 | 33 | update() { 34 | this.scroll.update(); 35 | } 36 | 37 | start() { 38 | this.scroll.startScroll(); 39 | } 40 | 41 | stop() { 42 | this.scroll.stopScroll(); 43 | } 44 | 45 | scrollTo(target, options) { 46 | this.scroll.scrollTo(target, options); 47 | } 48 | 49 | setScroll(x, y) { 50 | this.scroll.setScroll(x, y); 51 | } 52 | 53 | on(event, func) { 54 | this.scroll.setEvents(event, func); 55 | } 56 | 57 | off(event, func) { 58 | this.scroll.unsetEvents(event, func); 59 | } 60 | 61 | destroy() { 62 | this.scroll.destroy(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/scripts/options.js: -------------------------------------------------------------------------------- 1 | export const defaults = { 2 | el: document, 3 | name: 'scroll', 4 | offset: [0, 0], 5 | repeat: false, 6 | smooth: false, 7 | initPosition: { x: 0, y: 0 }, 8 | direction: 'vertical', 9 | gestureDirection: 'vertical', 10 | reloadOnContextChange: false, 11 | lerp: 0.1, 12 | class: 'is-inview', 13 | scrollbarContainer: false, 14 | scrollbarClass: 'c-scrollbar', 15 | scrollingClass: 'has-scroll-scrolling', 16 | draggingClass: 'has-scroll-dragging', 17 | smoothClass: 'has-scroll-smooth', 18 | initClass: 'has-scroll-init', 19 | getSpeed: false, 20 | getDirection: false, 21 | scrollFromAnywhere: false, 22 | multiplier: 1, 23 | firefoxMultiplier: 50, 24 | touchMultiplier: 2, 25 | resetNativeScroll: true, 26 | tablet: { 27 | smooth: false, 28 | direction: 'vertical', 29 | gestureDirection: 'vertical', 30 | breakpoint: 1024 31 | }, 32 | smartphone: { 33 | smooth: false, 34 | direction: 'vertical', 35 | gestureDirection: 'vertical' 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /src/scripts/utils/html.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns an array containing all the parent nodes of the given node 3 | * @param {object} node 4 | * @return {array} parent nodes 5 | */ 6 | export function getParents(elem) { 7 | // Set up a parent array 8 | let parents = []; 9 | 10 | // Push each parent element to the array 11 | for (; elem && elem !== document; elem = elem.parentNode) { 12 | parents.push(elem); 13 | } 14 | 15 | // Return our parent array 16 | return parents; 17 | } 18 | 19 | // https://gomakethings.com/how-to-get-the-closest-parent-element-with-a-matching-selector-using-vanilla-javascript/ 20 | export function queryClosestParent(elem, selector) { 21 | // Element.matches() polyfill 22 | if (!Element.prototype.matches) { 23 | Element.prototype.matches = 24 | Element.prototype.matchesSelector || 25 | Element.prototype.mozMatchesSelector || 26 | Element.prototype.msMatchesSelector || 27 | Element.prototype.oMatchesSelector || 28 | Element.prototype.webkitMatchesSelector || 29 | function (s) { 30 | var matches = (this.document || this.ownerDocument).querySelectorAll(s), 31 | i = matches.length; 32 | while (--i >= 0 && matches.item(i) !== this) {} 33 | return i > -1; 34 | }; 35 | } 36 | 37 | // Get the closest matching element 38 | for (; elem && elem !== document; elem = elem.parentNode) { 39 | if (elem.matches(selector)) return elem; 40 | } 41 | return null; 42 | } 43 | -------------------------------------------------------------------------------- /src/scripts/utils/maths.js: -------------------------------------------------------------------------------- 1 | export function lerp(start, end, amt) { 2 | return (1 - amt) * start + amt * end; 3 | } 4 | -------------------------------------------------------------------------------- /src/scripts/utils/transform.js: -------------------------------------------------------------------------------- 1 | export function transform(el, transformValue) { 2 | el.style.webkitTransform = transformValue; 3 | el.style.msTransform = transformValue; 4 | el.style.transform = transformValue; 5 | } 6 | 7 | export function getTranslate(el) { 8 | const translate = {}; 9 | if (!window.getComputedStyle) return; 10 | 11 | const style = getComputedStyle(el); 12 | const transform = style.transform || style.webkitTransform || style.mozTransform; 13 | 14 | let mat = transform.match(/^matrix3d\((.+)\)$/); 15 | if (mat) { 16 | translate.x = mat ? parseFloat(mat[1].split(', ')[12]) : 0; 17 | translate.y = mat ? parseFloat(mat[1].split(', ')[13]) : 0; 18 | } else { 19 | mat = transform.match(/^matrix\((.+)\)$/); 20 | translate.x = mat ? parseFloat(mat[1].split(', ')[4]) : 0; 21 | translate.y = mat ? parseFloat(mat[1].split(', ')[5]) : 0; 22 | } 23 | return translate; 24 | } 25 | -------------------------------------------------------------------------------- /src/styles/_base.scss: -------------------------------------------------------------------------------- 1 | html { 2 | &.has-scroll-smooth { 3 | overflow: hidden; 4 | } 5 | 6 | &.has-scroll-dragging { 7 | user-select: none; 8 | } 9 | } 10 | 11 | body { 12 | .has-scroll-smooth & { 13 | overflow: hidden; 14 | } 15 | } 16 | 17 | [data-scroll-container] { 18 | .has-scroll-smooth & { 19 | min-height: 100vh; 20 | } 21 | [data-scroll-direction="horizontal"] & { 22 | height: 100vh; 23 | display: inline-block; 24 | white-space: nowrap; 25 | } 26 | } 27 | 28 | [data-scroll-section] { 29 | [data-scroll-direction="horizontal"] & { 30 | display: inline-block; 31 | vertical-align: top; 32 | white-space: nowrap; 33 | height: 100%; 34 | } 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/styles/_scrollbar.scss: -------------------------------------------------------------------------------- 1 | .c-scrollbar { 2 | position: absolute; 3 | right: 0; 4 | top: 0; 5 | width: 11px; 6 | height: 100%; 7 | transform-origin: center right; 8 | transition: transform 0.3s, opacity 0.3s; 9 | opacity: 0; 10 | 11 | &:hover { 12 | transform: scaleX(1.45); 13 | } 14 | 15 | &:hover, .has-scroll-scrolling &, .has-scroll-dragging & { 16 | opacity: 1; 17 | } 18 | 19 | [data-scroll-direction="horizontal"] & { 20 | width: 100%; 21 | height: 10px; 22 | top: auto; 23 | bottom: 0; 24 | transform: scaleY(1); 25 | 26 | &:hover { 27 | transform: scaleY(1.3); 28 | } 29 | } 30 | 31 | } 32 | 33 | .c-scrollbar_thumb { 34 | position: absolute; 35 | top: 0; 36 | right: 0; 37 | background-color: black; 38 | opacity: 0.5; 39 | width: 7px; 40 | border-radius: 10px; 41 | margin: 2px; 42 | cursor: grab; 43 | 44 | .has-scroll-dragging & { 45 | cursor: grabbing; 46 | } 47 | 48 | [data-scroll-direction="horizontal"] & { 49 | right: auto; 50 | bottom: 0; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /www: -------------------------------------------------------------------------------- 1 | ./docs --------------------------------------------------------------------------------