├── .editorconfig ├── .eslintrc ├── .gitattributes ├── .github └── browsersync.png ├── .gitignore ├── .nvmrc ├── LICENSE ├── README.md ├── docs └── single-file-components.md ├── package.json ├── src ├── assets │ └── images │ │ ├── favicon.ico │ │ └── logo.png ├── components │ ├── Home │ │ ├── home.html │ │ └── home.js │ ├── Loader │ │ ├── Loader.js │ │ ├── loader.js │ │ └── loader.scss │ ├── Navigation │ │ ├── navigation.html │ │ └── navigation.js │ ├── NotFound │ │ └── notFound.js │ └── Posts │ │ ├── createPost.js │ │ ├── editPost.html │ │ ├── editPost.js │ │ ├── post.html │ │ ├── post.js │ │ ├── posts.html │ │ └── posts.js ├── config │ ├── constants.js │ ├── http.js │ └── loading-state.js ├── index.html ├── main.js ├── routes.js ├── style.scss ├── styles │ ├── _animate.scss │ ├── _global.scss │ └── _variables.scss └── util │ ├── helpers.js │ └── resources.js ├── webpack ├── development.js ├── plugins.js ├── production.js └── webpack.base.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "eslint:recommended", 4 | "globals": { 5 | "window": true, 6 | "document": true, 7 | "fetch": true, 8 | "Headers": true, 9 | "Request": true, 10 | "FormData": true, 11 | "FileReader": true, 12 | "localStorage": true 13 | }, 14 | "env": { 15 | "node": true, 16 | "es6": true, 17 | "amd": true, 18 | "browser": true, 19 | "jquery": true 20 | }, 21 | "parserOptions": { 22 | "ecmaFeatures": { 23 | "globalReturn": true, 24 | "generators": false, 25 | "objectLiteralDuplicateProperties": false, 26 | "experimentalObjectRestSpread": true 27 | }, 28 | "ecmaVersion": 2017, 29 | "sourceType": "module" 30 | }, 31 | "plugins": [ 32 | "import", 33 | "html", 34 | "jsx-a11y" 35 | ], 36 | "settings": { 37 | "import/core-modules": [], 38 | "import/ignore": [ 39 | "node_modules", 40 | "\\.(coffee|scss|css|less|hbs|svg|json)$" 41 | ] 42 | }, 43 | "rules": { 44 | "no-console": 0 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | src/styles/* linguist-vendored 2 | -------------------------------------------------------------------------------- /.github/browsersync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/villeristi/vue.js-starter-template/6f0088e9eedcb194ba7284c1f02a6e9fbd56d17b/.github/browsersync.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | *.log 4 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 8.6.0 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ville Ristimäki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue.js starter template 2 | 3 |

4 | 5 |

6 | 7 | A bare-bones starter-template to get your hands dirty with awesome [Vue.js](https://github.com/vuejs/vue) library. 8 | 9 | Built with: 10 | * [Vue.js 2](https://github.com/vuejs/vue) 11 | * [Vue Router 2](https://github.com/vuejs/vue-router) 12 | * [Axios](https://github.com/mzabriskie/axios) 13 | * [Animate.css](https://github.com/daneden/animate.css) 14 | * [Babel](https://babeljs.io/) 15 | * [Bootstrap 4](https://v4-alpha.getbootstrap.com/) 16 | * [BrowserSync](https://www.browsersync.io/) 17 | * [ESLint](http://eslint.org/) 18 | * [Font Awesome](http://fontawesome.io/) 19 | * [JSONPlaceholder](http://jsonplaceholder.typicode.com/) 20 | * [SASS](http://sass-lang.com/) 21 | * [Webpack 2](https://webpack.js.org/) 22 | * [Yarn](https://yarnpkg.com/en/docs/install) 23 | * ...and many more 24 | 25 | ## Getting started 26 | 27 | 1. Be sure you have [Yarn](https://yarnpkg.com/en/docs/install) installed globally. 28 | 2. Clone the repo & run `yarn` from the project root 29 | 30 | ## Single File Components 31 | See [instructions](docs/single-file-components.md) for example usage of [single file components](https://vuejs.org/v2/guide/single-file-components.html). 32 | 33 | ## Available commands 34 | 35 | ```sh 36 | yarn start 37 | ``` 38 | 39 | Runs the Webpack module-bundler, starts watching for changes & launches the BrowserSync server to [http://localhost:3000](http://localhost:3000) (it's possible to change the port from `package.json` config-section). Uses [Webpack Dashboard](https://github.com/FormidableLabs/webpack-dashboard) 40 | 41 | **Note!** Webpack handles all the reloading stuff while BrowserSync just proxies the default webpack-port (`8080`) giving the possibility to connect to dev-server from multiple devices: 42 | ![BrowserSync](.github/browsersync.png) 43 | 44 | 45 | ```sh 46 | yarn lint:js 47 | ``` 48 | 49 | Lints javascript-files inside `/src` directory 50 | 51 | ```sh 52 | yarn build 53 | ``` 54 | 55 | Runs the webpack module-bundler with production-settings (compress etc.) and builds the project to `/build` directory. 56 | 57 | ## Demo 58 | Navigate to [https://vue-starter.ville.io/](https://vue-starter.ville.io/) and see the awesomeness IRL :bowtie: 59 | -------------------------------------------------------------------------------- /docs/single-file-components.md: -------------------------------------------------------------------------------- 1 | # Single File Components 2 | 3 | It's also possible to define your components with a [single file components](https://vuejs.org/v2/guide/single-file-components.html) fashion. Below is an example how to turn the `Loader` into a single-file-component and make use of the `vue-loader`: 4 | 5 | 1. Remove all files from `src/components/Loader` 6 | 2. Add a `Loader.vue` -file populated with below content: 7 | 8 | ```vue 9 | 12 | 53 | 59 | ``` 60 | 61 | 3. Import the file with `.vue`-extension in `main.js`: 62 | ```javascript 63 | import Loader from 'components/Loader/Loader.vue'; 64 | ``` 65 | 66 | 4. Edit your webpack config `webpack/webpack.base.js` so that `.vue` files are linted. Look for `eslint-loader` part of module rules and change the `test` attribute: 67 | ```javascript 68 | { 69 | enforce: 'pre', 70 | exclude: /node_modules/, 71 | loader: 'eslint-loader', 72 | test: /\.(js?|vue)$/ 73 | }, 74 | 75 | ``` 76 | 77 | 5. Optionally, in the same config `webpack/webpack.base.js`, define `resolve.extensions` for [automatic extension resolution](https://webpack.js.org/configuration/resolve/#resolve-extensions) : 78 | ```javascript 79 | resolve: { 80 | alias: { 81 | ... 82 | }, 83 | extensions: ['*','.js','.vue'] 84 | }, 85 | ``` 86 | This will allow you to import `.js` and `.vue` files without having to specify their extension, i.e. 87 | ```javascript 88 | import Loader from 'components/Loader/Loader'; 89 | ``` 90 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue.js-starter-template", 3 | "version": "0.10.0", 4 | "description": "A starter for Vue.js-projects", 5 | "keywords": [ 6 | "vue.js", 7 | "vue.js router", 8 | "axios", 9 | "webpack 2", 10 | "animate.css" 11 | ], 12 | "author": "Wille Ristimäki ", 13 | "license": "MIT", 14 | "repository": { 15 | "type": "git", 16 | "url": "git@github.com:villeristi/vue.js-starter-template.git" 17 | }, 18 | "config": { 19 | "port": 3000 20 | }, 21 | "scripts": { 22 | "start": "webpack-dashboard -c magenta -t 'Vue.js-Starter-Template' -- webpack-dev-server --config webpack/development.js --progress --hot", 23 | "build": "webpack --config webpack/production.js -p --progress", 24 | "lint:js": "eslint src" 25 | }, 26 | "dependencies": { 27 | "axios": "^0.16.2", 28 | "bootstrap": "4.0.0-alpha.6", 29 | "css-hot-loader": "^1.3.1", 30 | "font-awesome": "^4.7.0", 31 | "vee-validate": "^2.0.0-rc.5", 32 | "vue": "^2.3.4", 33 | "vue-router": "^2.5.3" 34 | }, 35 | "devDependencies": { 36 | "babel-core": "^6.26.0", 37 | "babel-eslint": "^8.0.1", 38 | "babel-loader": "^7.0.0", 39 | "babel-plugin-transform-runtime": "^6.15.0", 40 | "babel-preset-es2015": "^6.24.1", 41 | "babel-preset-stage-2": "^6.24.1", 42 | "browser-sync": "^2.18.12", 43 | "browser-sync-webpack-plugin": "^1.1.3", 44 | "copy-webpack-plugin": "^4.0.1", 45 | "css-loader": "^0.28.7", 46 | "eslint": "^4.7.2", 47 | "eslint-friendly-formatter": "^3.0.0", 48 | "eslint-loader": "^1.7.1", 49 | "eslint-plugin-html": "^3.2.2", 50 | "eslint-plugin-import": "^2.3.0", 51 | "eslint-plugin-jsx-a11y": "^6.0.2", 52 | "exports-loader": "^0.6.3", 53 | "extract-text-webpack-plugin": "^3.0.0", 54 | "favicons-webpack-plugin": "0.0.7", 55 | "file-loader": "^0.11.2", 56 | "html-loader": "^0.5.1", 57 | "html-webpack-plugin": "^2.26.0", 58 | "imagemin-mozjpeg": "^6.0.0", 59 | "imagemin-webpack-plugin": "^1.2.1", 60 | "imports-loader": "^0.7.1", 61 | "minimist": "^1.2.0", 62 | "node-sass": "^4.5.3", 63 | "optimize-css-assets-webpack-plugin": "^1.3.2", 64 | "require-dir": "^0.3.2", 65 | "resolve-url-loader": "^2.1.0", 66 | "sass-loader": "^6.0.6", 67 | "source-map-loader": "^0.2.1", 68 | "style-loader": "^0.18.2", 69 | "url-loader": "^0.5.9", 70 | "vue-loader": "^13.0.5", 71 | "vue-template-compiler": "^2.3.4", 72 | "webpack": "^3.6.0", 73 | "webpack-cleanup-plugin": "^0.5.1", 74 | "webpack-dashboard": "^0.2.1", 75 | "webpack-dev-server": "^2.9.1", 76 | "webpack-merge": "^0.14.0", 77 | "webpack-notifier": "^1.5.0" 78 | }, 79 | "babel": { 80 | "presets": [ 81 | "es2015" 82 | ] 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/villeristi/vue.js-starter-template/6f0088e9eedcb194ba7284c1f02a6e9fbd56d17b/src/assets/images/favicon.ico -------------------------------------------------------------------------------- /src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/villeristi/vue.js-starter-template/6f0088e9eedcb194ba7284c1f02a6e9fbd56d17b/src/assets/images/logo.png -------------------------------------------------------------------------------- /src/components/Home/home.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

My awesome project

4 | 5 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut dictum accumsan lorem in aliquam. Vestibulum ante 6 | ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus orci orci, cursus vulputate 7 | hendrerit eu, dapibus eu nisi. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos 8 | himenaeos. Nullam placerat dapibus tempor. Suspendisse nec mi at erat tincidunt mattis ut nec lacus. Praesent 9 | non nisl sed libero convallis tincidunt. Curabitur commodo aliquam vestibulum. Etiam rutrum risus quam, eget 10 | faucibus diam ornare in.

11 | 12 |

Morbi sollicitudin enim a rhoncus varius. Sed a lacus eu diam dapibus pellentesque sit amet at sem. Sed 13 | scelerisque pretium metus, sed mollis sem molestie eu. Suspendisse iaculis non erat ac consectetur. Phasellus 14 | placerat, eros ornare dictum efficitur, mauris dui varius risus, ac dignissim erat dolor lobortis augue. 15 | Praesent lectus augue, dignissim et sagittis in, convallis id nisl. Morbi interdum neque nec massa facilisis, ut 16 | rutrum justo semper. Phasellus fringilla auctor blandit. Maecenas nibh mi, cursus sed velit id, sodales semper 17 | sapien. Curabitur vitae nisl porta, tempus neque vitae, finibus nulla. Nam condimentum elit vel enim commodo, a 18 | tincidunt ante ornare. Duis libero sem, ultricies eu neque id, dapibus accumsan lectus.

19 | 20 |
21 | -------------------------------------------------------------------------------- /src/components/Home/home.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import template from './home.html'; 3 | 4 | export default Vue.extend({ 5 | template, 6 | }); 7 | -------------------------------------------------------------------------------- /src/components/Loader/Loader.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import './loader.scss'; 4 | 5 | export default Vue.extend({ 6 | template: '
' 7 | }); 8 | -------------------------------------------------------------------------------- /src/components/Loader/loader.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import './loader.scss'; 4 | 5 | export default Vue.extend({ 6 | template: '
' 7 | }); 8 | -------------------------------------------------------------------------------- /src/components/Loader/loader.scss: -------------------------------------------------------------------------------- 1 | $vue-green: #41b883; 2 | 3 | .loader { 4 | font-size: 10px; 5 | position: absolute; 6 | z-index: 9999; // Above everything... 7 | top: 50%; 8 | left: 50%; 9 | margin: -20px 0 0 -20px; 10 | text-indent: -9999em; 11 | border-top: 5px solid lighten($vue-green, 35%); 12 | border-right: 5px solid lighten($vue-green, 35%); 13 | border-bottom: 5px solid lighten($vue-green, 35%); 14 | border-left: 5px solid $vue-green; 15 | -webkit-transform: translateZ(0); 16 | -ms-transform: translateZ(0); 17 | transform: translateZ(0); 18 | -webkit-animation: loaderSpinner 1.1s infinite linear; 19 | animation: loaderSpinner 1.1s infinite linear; 20 | 21 | &, 22 | &:after { 23 | border-radius: 50%; 24 | width: 40px; 25 | height: 40px 26 | } 27 | } 28 | 29 | @-webkit-keyframes loaderSpinner { 30 | 0% { 31 | -webkit-transform: rotate(0deg); 32 | transform: rotate(0deg); 33 | } 34 | 100% { 35 | -webkit-transform: rotate(360deg); 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/Navigation/navigation.html: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /src/components/Navigation/navigation.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import template from './navigation.html'; 3 | 4 | export default Vue.extend({ 5 | template, 6 | }); 7 | -------------------------------------------------------------------------------- /src/components/NotFound/notFound.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | export default Vue.extend({ 4 | template: ` 5 |
6 |

Not found...

7 |

Sorry

8 |
9 | `, 10 | }); 11 | -------------------------------------------------------------------------------- /src/components/Posts/createPost.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import VeeValidate from 'vee-validate'; 3 | 4 | Vue.use(VeeValidate); 5 | 6 | import { postsResource } from 'src/util/resources'; 7 | import template from './editPost.html'; 8 | 9 | export default Vue.extend({ 10 | template, 11 | 12 | data() { 13 | return { 14 | post: {}, 15 | message: null 16 | }; 17 | }, 18 | 19 | computed: { 20 | isDirty() { 21 | return Object.keys(this.fields).some(key => this.fields[key].dirty); 22 | } 23 | }, 24 | 25 | methods: { 26 | handleSubmit(){ 27 | this.$validator.validateAll().then((success) => { 28 | if (success) { 29 | return this.savePost(); 30 | } 31 | 32 | return this; 33 | }); 34 | }, 35 | 36 | showMessage(message = {}, timeout = 2000){ 37 | this.message = message; 38 | setTimeout(() => { 39 | this.message = null; 40 | }, timeout); 41 | }, 42 | 43 | savePost(){ 44 | return postsResource.post('/', this.post) 45 | .then((response) => { 46 | this.post = response.data; 47 | 48 | this.showMessage({ 49 | type: 'success', 50 | text: 'Post created!' 51 | }); 52 | 53 | // TODO: We need to reset the form after success.... 54 | // this.fields.reset(); 55 | }) 56 | .catch((errorResponse) => { 57 | // Handle error... 58 | this.showMessage({ 59 | type: 'danger', 60 | text: errorResponse 61 | }); 62 | console.log('API responded with:', errorResponse); 63 | }); 64 | } 65 | } 66 | }); 67 | -------------------------------------------------------------------------------- /src/components/Posts/editPost.html: -------------------------------------------------------------------------------- 1 | 4 |
5 |
6 |

{{ post.title }}

7 |
8 |
9 |
10 | 11 | 15 |
16 | 19 |
20 |
21 | 22 | 26 | 29 | 30 | 31 |
32 | 33 |
34 | 42 |
43 |
44 | 45 |
46 | 47 |
48 | 56 |
57 |
58 | 59 |
60 |
61 |
62 | 65 |
66 |
67 | 68 |
69 |
70 | 71 | Back to posts 72 | 73 |
74 |
75 | -------------------------------------------------------------------------------- /src/components/Posts/editPost.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import VeeValidate from 'vee-validate'; 3 | 4 | Vue.use(VeeValidate); 5 | 6 | import { postsResource } from 'src/util/resources'; 7 | import template from './editPost.html'; 8 | 9 | export default Vue.extend({ 10 | template, 11 | 12 | data() { 13 | return { 14 | post: {}, 15 | message: null, 16 | id: this.$route.params.id, 17 | }; 18 | }, 19 | 20 | computed: { 21 | isDirty() { 22 | return Object.keys(this.fields).some(key => this.fields[key].dirty); 23 | } 24 | }, 25 | 26 | created(){ 27 | this.fetchPost(); 28 | }, 29 | 30 | methods: { 31 | handleSubmit(){ 32 | this.$validator.validateAll().then((success) => { 33 | if (success) { 34 | return this.savePost(); 35 | } 36 | 37 | return this; 38 | }); 39 | }, 40 | 41 | showMessage(message = {}, timeout = 2000){ 42 | this.message = message; 43 | setTimeout(() => { 44 | this.message = null; 45 | }, timeout); 46 | }, 47 | 48 | savePost(){ 49 | return postsResource.put(`${this.id}`, this.post) 50 | .then((response) => { 51 | this.post = response.data; 52 | 53 | this.showMessage({ 54 | type: 'success', 55 | text: 'Post updated!' 56 | }); 57 | 58 | // TODO: We need to reset the form after success.... 59 | // this.fields.reset(); 60 | }) 61 | .catch((errorResponse) => { 62 | // Handle error... 63 | this.showMessage({ 64 | type: 'danger', 65 | text: errorResponse 66 | }); 67 | console.log('API responded with:', errorResponse); 68 | }); 69 | }, 70 | 71 | fetchPost(){ 72 | return postsResource.get(`${this.id}`) 73 | .then((response) => { 74 | this.post = response.data; 75 | }) 76 | .catch((errorResponse) => { 77 | // Handle error... 78 | console.log('API responded with:', errorResponse); 79 | }); 80 | } 81 | } 82 | }); 83 | -------------------------------------------------------------------------------- /src/components/Posts/post.html: -------------------------------------------------------------------------------- 1 | 4 |
5 |
6 |

{{ post.title }}

7 |

{{ post.body }}

8 |
9 |
10 | 11 | Back to posts 12 | 13 | 14 | Edit 15 | 16 |
17 |
18 | -------------------------------------------------------------------------------- /src/components/Posts/post.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import { postsResource } from 'src/util/resources'; 4 | import template from './post.html'; 5 | 6 | export default Vue.extend({ 7 | template, 8 | 9 | data() { 10 | return { 11 | post: {} 12 | }; 13 | }, 14 | 15 | created(){ 16 | this.fetchPost(); 17 | }, 18 | 19 | methods: { 20 | fetchPost(){ 21 | const id = this.$route.params.id; 22 | 23 | return postsResource.get(`${id}`) 24 | .then((response) => { 25 | this.post = response.data; 26 | }) 27 | .catch((errorResponse) => { 28 | // Handle error... 29 | console.log('API responded with:', errorResponse); 30 | }); 31 | } 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /src/components/Posts/posts.html: -------------------------------------------------------------------------------- 1 |
2 |

Posts

3 | 4 |
5 | 6 |
7 | 8 | 14 | 20 | #{{ index+1 }} {{ post.title }} 21 | 22 | 23 |
24 | -------------------------------------------------------------------------------- /src/components/Posts/posts.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import { postsResource } from 'src/util/resources'; 4 | import template from './posts.html'; 5 | 6 | const animation = 'flipInX'; 7 | const animationDelay = 25; // in ms 8 | 9 | export default Vue.extend({ 10 | template, 11 | 12 | data() { 13 | return { 14 | postsFilter: '', 15 | posts: [] 16 | }; 17 | }, 18 | 19 | computed: { 20 | filteredPosts() { 21 | return this.posts.filter((post) => post.title.toLowerCase().indexOf(this.postsFilter.toLowerCase()) !== -1); 22 | } 23 | }, 24 | 25 | created(){ 26 | this.fetchPosts(); 27 | }, 28 | 29 | methods: { 30 | fetchPosts(){ 31 | return postsResource.get('/') 32 | .then((response) => { 33 | this.posts = response.data; 34 | }) 35 | .catch((errorResponse) => { 36 | // Handle error... 37 | console.log('API responded with:', errorResponse); 38 | }); 39 | }, 40 | 41 | // Methods for transitions 42 | handleBeforeEnter(el) { 43 | el.style.opacity = 0; 44 | el.classList.add('animated'); 45 | }, 46 | 47 | handleEnter(el) { 48 | const delay = el.dataset.index * animationDelay; 49 | setTimeout(() => { 50 | el.style.opacity = 1; 51 | el.classList.add(animation); 52 | }, delay); 53 | } 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /src/config/constants.js: -------------------------------------------------------------------------------- 1 | // A global API-root 2 | export const API_BASE = 'https://jsonplaceholder.typicode.com'; 3 | -------------------------------------------------------------------------------- /src/config/http.js: -------------------------------------------------------------------------------- 1 | import { setLoading } from 'src/util/helpers'; 2 | import { postsResource } from 'src/util/resources'; 3 | 4 | // Request interceptor 5 | postsResource.interceptors.request.use((config) => { 6 | setLoading(true); 7 | return config; 8 | }, (error) => { 9 | setLoading(false); 10 | console.log('RequestError: ', error); 11 | // Do something with request error 12 | return Promise.reject(error); 13 | }); 14 | 15 | // Response interceptor 16 | postsResource.interceptors.response.use((response) => { 17 | setLoading(false); 18 | return response; 19 | }, (error) => { 20 | setLoading(false); 21 | console.log('ResponseError: ', error); 22 | // Do something with response error 23 | return Promise.reject(error); 24 | }); 25 | -------------------------------------------------------------------------------- /src/config/loading-state.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | // Loading state handler 4 | export const LoadingState = new Vue(); 5 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Vue.js starter 9 | 10 | 11 | Fork me on GitHub 12 |
13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |
21 |
22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import VueRouter from 'vue-router'; 3 | 4 | import { LoadingState } from 'src/config/loading-state'; 5 | import Navigation from 'components/Navigation/navigation'; 6 | import Loader from 'components/Loader/loader'; 7 | 8 | Vue.use(VueRouter); 9 | 10 | import 'src/config/http'; 11 | import routes from 'src/routes'; 12 | import 'src/style.scss'; 13 | 14 | export const router = new VueRouter({ 15 | routes, 16 | mode: 'history', 17 | linkActiveClass: 'active' 18 | }); 19 | 20 | new Vue({ 21 | router, 22 | components: { 23 | Navigation, 24 | Loader 25 | }, 26 | 27 | data(){ 28 | return { 29 | isLoading: false 30 | }; 31 | }, 32 | 33 | created(){ 34 | LoadingState.$on('toggle', (isLoading) => { 35 | this.isLoading = isLoading; 36 | }); 37 | } 38 | }).$mount('#app'); 39 | -------------------------------------------------------------------------------- /src/routes.js: -------------------------------------------------------------------------------- 1 | import Home from 'components/Home/home'; 2 | import Posts from 'components/Posts/posts'; 3 | import Post from 'components/Posts/post'; 4 | import CreatePost from 'components/Posts/createPost'; 5 | import EditPost from 'components/Posts/editPost'; 6 | import NotFound from 'components/NotFound/notFound'; 7 | 8 | const routes = [ 9 | { 10 | path: '/', 11 | component: Home 12 | }, 13 | { 14 | path: '/posts', 15 | component: Posts 16 | }, 17 | { 18 | path: '/posts/create', 19 | name: 'createPost', 20 | component: CreatePost 21 | }, 22 | { 23 | path: '/post/:id', 24 | name: 'post', 25 | component: Post 26 | }, 27 | { 28 | path: '/post/:id/edit', 29 | name: 'editPost', 30 | component: EditPost 31 | }, 32 | { 33 | path: '*', 34 | component: NotFound 35 | } 36 | ]; 37 | 38 | export default routes; 39 | -------------------------------------------------------------------------------- /src/style.scss: -------------------------------------------------------------------------------- 1 | @import "styles/variables"; 2 | 3 | // npm depencies 4 | @import "~bootstrap/scss/bootstrap"; 5 | @import "~font-awesome/scss/font-awesome"; 6 | 7 | // Project specific 8 | @import "styles/global"; 9 | @import "styles/animate"; 10 | -------------------------------------------------------------------------------- /src/styles/_animate.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /*! 4 | * animate.css -http://daneden.me/animate 5 | * Version - 3.5.0 6 | * Licensed under the MIT license - http://opensource.org/licenses/MIT 7 | * 8 | * Copyright (c) 2016 Daniel Eden 9 | */ 10 | 11 | .animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.animated.infinite{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.animated.hinge{-webkit-animation-duration:2s;animation-duration:2s}.animated.bounceIn,.animated.bounceOut,.animated.flipOutX,.animated.flipOutY{-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes bounce{0%,20%,53%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1);-webkit-transform:translateZ(0);transform:translateZ(0)}40%,43%{-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}40%,43%,70%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06)}70%{-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}@keyframes bounce{0%,20%,53%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1);-webkit-transform:translateZ(0);transform:translateZ(0)}40%,43%{-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}40%,43%,70%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06)}70%{-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}.bounce{-webkit-animation-name:bounce;animation-name:bounce;-webkit-transform-origin:center bottom;transform-origin:center bottom}@-webkit-keyframes flash{0%,50%,to{opacity:1}25%,75%{opacity:0}}@keyframes flash{0%,50%,to{opacity:1}25%,75%{opacity:0}}.flash{-webkit-animation-name:flash;animation-name:flash}@-webkit-keyframes pulse{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes pulse{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}.pulse{-webkit-animation-name:pulse;animation-name:pulse}@-webkit-keyframes rubberBand{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes rubberBand{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}.rubberBand{-webkit-animation-name:rubberBand;animation-name:rubberBand}@-webkit-keyframes shake{0%,to{-webkit-transform:translateZ(0);transform:translateZ(0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}@keyframes shake{0%,to{-webkit-transform:translateZ(0);transform:translateZ(0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}.shake{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes headShake{0%{-webkit-transform:translateX(0);transform:translateX(0)}6.5%{-webkit-transform:translateX(-6px) rotateY(-9deg);transform:translateX(-6px) rotateY(-9deg)}18.5%{-webkit-transform:translateX(5px) rotateY(7deg);transform:translateX(5px) rotateY(7deg)}31.5%{-webkit-transform:translateX(-3px) rotateY(-5deg);transform:translateX(-3px) rotateY(-5deg)}43.5%{-webkit-transform:translateX(2px) rotateY(3deg);transform:translateX(2px) rotateY(3deg)}50%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes headShake{0%{-webkit-transform:translateX(0);transform:translateX(0)}6.5%{-webkit-transform:translateX(-6px) rotateY(-9deg);transform:translateX(-6px) rotateY(-9deg)}18.5%{-webkit-transform:translateX(5px) rotateY(7deg);transform:translateX(5px) rotateY(7deg)}31.5%{-webkit-transform:translateX(-3px) rotateY(-5deg);transform:translateX(-3px) rotateY(-5deg)}43.5%{-webkit-transform:translateX(2px) rotateY(3deg);transform:translateX(2px) rotateY(3deg)}50%{-webkit-transform:translateX(0);transform:translateX(0)}}.headShake{-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-animation-name:headShake;animation-name:headShake}@-webkit-keyframes swing{20%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}40%{-webkit-transform:rotate(-10deg);transform:rotate(-10deg)}60%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}80%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes swing{20%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}40%{-webkit-transform:rotate(-10deg);transform:rotate(-10deg)}60%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}80%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}.swing{-webkit-transform-origin:top center;transform-origin:top center;-webkit-animation-name:swing;animation-name:swing}@-webkit-keyframes tada{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate(-3deg);transform:scale3d(.9,.9,.9) rotate(-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate(3deg);transform:scale3d(1.1,1.1,1.1) rotate(3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate(-3deg);transform:scale3d(1.1,1.1,1.1) rotate(-3deg)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes tada{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate(-3deg);transform:scale3d(.9,.9,.9) rotate(-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate(3deg);transform:scale3d(1.1,1.1,1.1) rotate(3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate(-3deg);transform:scale3d(1.1,1.1,1.1) rotate(-3deg)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}.tada{-webkit-animation-name:tada;animation-name:tada}@-webkit-keyframes wobble{0%{-webkit-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate(-5deg);transform:translate3d(-25%,0,0) rotate(-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate(3deg);transform:translate3d(20%,0,0) rotate(3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate(-3deg);transform:translate3d(-15%,0,0) rotate(-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate(2deg);transform:translate3d(10%,0,0) rotate(2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate(-1deg);transform:translate3d(-5%,0,0) rotate(-1deg)}to{-webkit-transform:none;transform:none}}@keyframes wobble{0%{-webkit-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate(-5deg);transform:translate3d(-25%,0,0) rotate(-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate(3deg);transform:translate3d(20%,0,0) rotate(3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate(-3deg);transform:translate3d(-15%,0,0) rotate(-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate(2deg);transform:translate3d(10%,0,0) rotate(2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate(-1deg);transform:translate3d(-5%,0,0) rotate(-1deg)}to{-webkit-transform:none;transform:none}}.wobble{-webkit-animation-name:wobble;animation-name:wobble}@-webkit-keyframes jello{0%,11.1%,to{-webkit-transform:none;transform:none}22.2%{-webkit-transform:skewX(-12.5deg) skewY(-12.5deg);transform:skewX(-12.5deg) skewY(-12.5deg)}33.3%{-webkit-transform:skewX(6.25deg) skewY(6.25deg);transform:skewX(6.25deg) skewY(6.25deg)}44.4%{-webkit-transform:skewX(-3.125deg) skewY(-3.125deg);transform:skewX(-3.125deg) skewY(-3.125deg)}55.5%{-webkit-transform:skewX(1.5625deg) skewY(1.5625deg);transform:skewX(1.5625deg) skewY(1.5625deg)}66.6%{-webkit-transform:skewX(-.78125deg) skewY(-.78125deg);transform:skewX(-.78125deg) skewY(-.78125deg)}77.7%{-webkit-transform:skewX(.390625deg) skewY(.390625deg);transform:skewX(.390625deg) skewY(.390625deg)}88.8%{-webkit-transform:skewX(-.1953125deg) skewY(-.1953125deg);transform:skewX(-.1953125deg) skewY(-.1953125deg)}}@keyframes jello{0%,11.1%,to{-webkit-transform:none;transform:none}22.2%{-webkit-transform:skewX(-12.5deg) skewY(-12.5deg);transform:skewX(-12.5deg) skewY(-12.5deg)}33.3%{-webkit-transform:skewX(6.25deg) skewY(6.25deg);transform:skewX(6.25deg) skewY(6.25deg)}44.4%{-webkit-transform:skewX(-3.125deg) skewY(-3.125deg);transform:skewX(-3.125deg) skewY(-3.125deg)}55.5%{-webkit-transform:skewX(1.5625deg) skewY(1.5625deg);transform:skewX(1.5625deg) skewY(1.5625deg)}66.6%{-webkit-transform:skewX(-.78125deg) skewY(-.78125deg);transform:skewX(-.78125deg) skewY(-.78125deg)}77.7%{-webkit-transform:skewX(.390625deg) skewY(.390625deg);transform:skewX(.390625deg) skewY(.390625deg)}88.8%{-webkit-transform:skewX(-.1953125deg) skewY(-.1953125deg);transform:skewX(-.1953125deg) skewY(-.1953125deg)}}.jello{-webkit-animation-name:jello;animation-name:jello;-webkit-transform-origin:center;transform-origin:center}@-webkit-keyframes bounceIn{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}to{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes bounceIn{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}to{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}}.bounceIn{-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes bounceInDown{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:none;transform:none}}@keyframes bounceInDown{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:none;transform:none}}.bounceInDown{-webkit-animation-name:bounceInDown;animation-name:bounceInDown}@-webkit-keyframes bounceInLeft{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}to{-webkit-transform:none;transform:none}}@keyframes bounceInLeft{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}to{-webkit-transform:none;transform:none}}.bounceInLeft{-webkit-animation-name:bounceInLeft;animation-name:bounceInLeft}@-webkit-keyframes bounceInRight{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}to{-webkit-transform:none;transform:none}}@keyframes bounceInRight{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}to{-webkit-transform:none;transform:none}}.bounceInRight{-webkit-animation-name:bounceInRight;animation-name:bounceInRight}@-webkit-keyframes bounceInUp{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes bounceInUp{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.bounceInUp{-webkit-animation-name:bounceInUp;animation-name:bounceInUp}@-webkit-keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}to{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}@keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}to{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}.bounceOut{-webkit-animation-name:bounceOut;animation-name:bounceOut}@-webkit-keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.bounceOutDown{-webkit-animation-name:bounceOutDown;animation-name:bounceOutDown}@-webkit-keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.bounceOutLeft{-webkit-animation-name:bounceOutLeft;animation-name:bounceOutLeft}@-webkit-keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.bounceOutRight{-webkit-animation-name:bounceOutRight;animation-name:bounceOutRight}@-webkit-keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.bounceOutUp{-webkit-animation-name:bounceOutUp;animation-name:bounceOutUp}@-webkit-keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}@-webkit-keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInDownBig{-webkit-animation-name:fadeInDownBig;animation-name:fadeInDownBig}@-webkit-keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInLeft{-webkit-animation-name:fadeInLeft;animation-name:fadeInLeft}@-webkit-keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInLeftBig{-webkit-animation-name:fadeInLeftBig;animation-name:fadeInLeftBig}@-webkit-keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInRight{-webkit-animation-name:fadeInRight;animation-name:fadeInRight}@-webkit-keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInRightBig{-webkit-animation-name:fadeInRightBig;animation-name:fadeInRightBig}@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInUp{-webkit-animation-name:fadeInUp;animation-name:fadeInUp}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInUpBig{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes fadeOut{0%{opacity:1}to{opacity:0}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOutDown{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes fadeOutDown{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.fadeOutDown{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown}@-webkit-keyframes fadeOutDownBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes fadeOutDownBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.fadeOutDownBig{-webkit-animation-name:fadeOutDownBig;animation-name:fadeOutDownBig}@-webkit-keyframes fadeOutLeft{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes fadeOutLeft{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.fadeOutLeft{-webkit-animation-name:fadeOutLeft;animation-name:fadeOutLeft}@-webkit-keyframes fadeOutLeftBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes fadeOutLeftBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.fadeOutLeftBig{-webkit-animation-name:fadeOutLeftBig;animation-name:fadeOutLeftBig}@-webkit-keyframes fadeOutRight{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes fadeOutRight{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.fadeOutRight{-webkit-animation-name:fadeOutRight;animation-name:fadeOutRight}@-webkit-keyframes fadeOutRightBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes fadeOutRightBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.fadeOutRightBig{-webkit-animation-name:fadeOutRightBig;animation-name:fadeOutRightBig}@-webkit-keyframes fadeOutUp{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes fadeOutUp{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}@-webkit-keyframes fadeOutUpBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes fadeOutUpBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.fadeOutUpBig{-webkit-animation-name:fadeOutUpBig;animation-name:fadeOutUpBig}@-webkit-keyframes flip{0%{-webkit-transform:perspective(400px) rotateY(-1turn);transform:perspective(400px) rotateY(-1turn)}0%,40%{-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translateZ(150px) rotateY(-190deg);transform:perspective(400px) translateZ(150px) rotateY(-190deg)}50%{-webkit-transform:perspective(400px) translateZ(150px) rotateY(-170deg);transform:perspective(400px) translateZ(150px) rotateY(-170deg)}50%,80%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95);transform:perspective(400px) scale3d(.95,.95,.95)}to{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}@keyframes flip{0%{-webkit-transform:perspective(400px) rotateY(-1turn);transform:perspective(400px) rotateY(-1turn)}0%,40%{-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translateZ(150px) rotateY(-190deg);transform:perspective(400px) translateZ(150px) rotateY(-190deg)}50%{-webkit-transform:perspective(400px) translateZ(150px) rotateY(-170deg);transform:perspective(400px) translateZ(150px) rotateY(-170deg)}50%,80%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95);transform:perspective(400px) scale3d(.95,.95,.95)}to{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}.animated.flip{-webkit-backface-visibility:visible;backface-visibility:visible;-webkit-animation-name:flip;animation-name:flip}@-webkit-keyframes flipInX{0%{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);opacity:0}0%,40%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg)}60%{-webkit-transform:perspective(400px) rotateX(10deg);transform:perspective(400px) rotateX(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateX(-5deg);transform:perspective(400px) rotateX(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInX{0%{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);opacity:0}0%,40%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg)}60%{-webkit-transform:perspective(400px) rotateX(10deg);transform:perspective(400px) rotateX(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateX(-5deg);transform:perspective(400px) rotateX(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}.flipInX{-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInX;animation-name:flipInX}@-webkit-keyframes flipInY{0%{-webkit-transform:perspective(400px) rotateY(90deg);transform:perspective(400px) rotateY(90deg);opacity:0}0%,40%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotateY(-20deg);transform:perspective(400px) rotateY(-20deg)}60%{-webkit-transform:perspective(400px) rotateY(10deg);transform:perspective(400px) rotateY(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateY(-5deg);transform:perspective(400px) rotateY(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInY{0%{-webkit-transform:perspective(400px) rotateY(90deg);transform:perspective(400px) rotateY(90deg);opacity:0}0%,40%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotateY(-20deg);transform:perspective(400px) rotateY(-20deg)}60%{-webkit-transform:perspective(400px) rotateY(10deg);transform:perspective(400px) rotateY(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateY(-5deg);transform:perspective(400px) rotateY(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}.flipInY{-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInY;animation-name:flipInY}@-webkit-keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);opacity:1}to{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);opacity:0}}@keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);opacity:1}to{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);opacity:0}}.flipOutX{-webkit-animation-name:flipOutX;animation-name:flipOutX;-webkit-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateY(-15deg);transform:perspective(400px) rotateY(-15deg);opacity:1}to{-webkit-transform:perspective(400px) rotateY(90deg);transform:perspective(400px) rotateY(90deg);opacity:0}}@keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateY(-15deg);transform:perspective(400px) rotateY(-15deg);opacity:1}to{-webkit-transform:perspective(400px) rotateY(90deg);transform:perspective(400px) rotateY(90deg);opacity:0}}.flipOutY{-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipOutY;animation-name:flipOutY}@-webkit-keyframes lightSpeedIn{0%{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);transform:skewX(20deg)}60%,80%{opacity:1}80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg)}to{-webkit-transform:none;transform:none;opacity:1}}@keyframes lightSpeedIn{0%{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);transform:skewX(20deg)}60%,80%{opacity:1}80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg)}to{-webkit-transform:none;transform:none;opacity:1}}.lightSpeedIn{-webkit-animation-name:lightSpeedIn;animation-name:lightSpeedIn;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}@-webkit-keyframes lightSpeedOut{0%{opacity:1}to{-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}@keyframes lightSpeedOut{0%{opacity:1}to{-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}.lightSpeedOut{-webkit-animation-name:lightSpeedOut;animation-name:lightSpeedOut;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}@-webkit-keyframes rotateIn{0%{transform-origin:center;-webkit-transform:rotate(-200deg);transform:rotate(-200deg);opacity:0}0%,to{-webkit-transform-origin:center}to{transform-origin:center;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateIn{0%{transform-origin:center;-webkit-transform:rotate(-200deg);transform:rotate(-200deg);opacity:0}0%,to{-webkit-transform-origin:center}to{transform-origin:center;-webkit-transform:none;transform:none;opacity:1}}.rotateIn{-webkit-animation-name:rotateIn;animation-name:rotateIn}@-webkit-keyframes rotateInDownLeft{0%{transform-origin:left bottom;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInDownLeft{0%{transform-origin:left bottom;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}.rotateInDownLeft{-webkit-animation-name:rotateInDownLeft;animation-name:rotateInDownLeft}@-webkit-keyframes rotateInDownRight{0%{transform-origin:right bottom;-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInDownRight{0%{transform-origin:right bottom;-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}.rotateInDownRight{-webkit-animation-name:rotateInDownRight;animation-name:rotateInDownRight}@-webkit-keyframes rotateInUpLeft{0%{transform-origin:left bottom;-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInUpLeft{0%{transform-origin:left bottom;-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}.rotateInUpLeft{-webkit-animation-name:rotateInUpLeft;animation-name:rotateInUpLeft}@-webkit-keyframes rotateInUpRight{0%{transform-origin:right bottom;-webkit-transform:rotate(-90deg);transform:rotate(-90deg);opacity:0}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInUpRight{0%{transform-origin:right bottom;-webkit-transform:rotate(-90deg);transform:rotate(-90deg);opacity:0}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}.rotateInUpRight{-webkit-animation-name:rotateInUpRight;animation-name:rotateInUpRight}@-webkit-keyframes rotateOut{0%{transform-origin:center;opacity:1}0%,to{-webkit-transform-origin:center}to{transform-origin:center;-webkit-transform:rotate(200deg);transform:rotate(200deg);opacity:0}}@keyframes rotateOut{0%{transform-origin:center;opacity:1}0%,to{-webkit-transform-origin:center}to{transform-origin:center;-webkit-transform:rotate(200deg);transform:rotate(200deg);opacity:0}}.rotateOut{-webkit-animation-name:rotateOut;animation-name:rotateOut}@-webkit-keyframes rotateOutDownLeft{0%{transform-origin:left bottom;opacity:1}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}}@keyframes rotateOutDownLeft{0%{transform-origin:left bottom;opacity:1}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}}.rotateOutDownLeft{-webkit-animation-name:rotateOutDownLeft;animation-name:rotateOutDownLeft}@-webkit-keyframes rotateOutDownRight{0%{transform-origin:right bottom;opacity:1}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}}@keyframes rotateOutDownRight{0%{transform-origin:right bottom;opacity:1}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}}.rotateOutDownRight{-webkit-animation-name:rotateOutDownRight;animation-name:rotateOutDownRight}@-webkit-keyframes rotateOutUpLeft{0%{transform-origin:left bottom;opacity:1}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}}@keyframes rotateOutUpLeft{0%{transform-origin:left bottom;opacity:1}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}}.rotateOutUpLeft{-webkit-animation-name:rotateOutUpLeft;animation-name:rotateOutUpLeft}@-webkit-keyframes rotateOutUpRight{0%{transform-origin:right bottom;opacity:1}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:rotate(90deg);transform:rotate(90deg);opacity:0}}@keyframes rotateOutUpRight{0%{transform-origin:right bottom;opacity:1}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:rotate(90deg);transform:rotate(90deg);opacity:0}}.rotateOutUpRight{-webkit-animation-name:rotateOutUpRight;animation-name:rotateOutUpRight}@-webkit-keyframes hinge{0%{transform-origin:top left}0%,20%,60%{-webkit-transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate(80deg);transform:rotate(80deg);transform-origin:top left}40%,80%{-webkit-transform:rotate(60deg);transform:rotate(60deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}to{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}@keyframes hinge{0%{transform-origin:top left}0%,20%,60%{-webkit-transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate(80deg);transform:rotate(80deg);transform-origin:top left}40%,80%{-webkit-transform:rotate(60deg);transform:rotate(60deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}to{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}.hinge{-webkit-animation-name:hinge;animation-name:hinge}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate(-120deg);transform:translate3d(-100%,0,0) rotate(-120deg)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate(-120deg);transform:translate3d(-100%,0,0) rotate(-120deg)}to{opacity:1;-webkit-transform:none;transform:none}}.rollIn{-webkit-animation-name:rollIn;animation-name:rollIn}@-webkit-keyframes rollOut{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate(120deg);transform:translate3d(100%,0,0) rotate(120deg)}}@keyframes rollOut{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate(120deg);transform:translate3d(100%,0,0) rotate(120deg)}}.rollOut{-webkit-animation-name:rollOut;animation-name:rollOut}@-webkit-keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}.zoomIn{-webkit-animation-name:zoomIn;animation-name:zoomIn}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInDown{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInLeft{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInRight{-webkit-animation-name:zoomInRight;animation-name:zoomInRight}@-webkit-keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInUp{-webkit-animation-name:zoomInUp;animation-name:zoomInUp}@-webkit-keyframes zoomOut{0%{opacity:1}50%{-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%,to{opacity:0}}@keyframes zoomOut{0%{opacity:1}50%{-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%,to{opacity:0}}.zoomOut{-webkit-animation-name:zoomOut;animation-name:zoomOut}@-webkit-keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomOutDown{-webkit-animation-name:zoomOutDown;animation-name:zoomOutDown}@-webkit-keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}@keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}.zoomOutLeft{-webkit-animation-name:zoomOutLeft;animation-name:zoomOutLeft}@-webkit-keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}@keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}.zoomOutRight{-webkit-animation-name:zoomOutRight;animation-name:zoomOutRight}@-webkit-keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomOutUp{-webkit-animation-name:zoomOutUp;animation-name:zoomOutUp}@-webkit-keyframes slideInDown{0%{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes slideInDown{0%{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.slideInDown{-webkit-animation-name:slideInDown;animation-name:slideInDown}@-webkit-keyframes slideInLeft{0%{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes slideInLeft{0%{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.slideInLeft{-webkit-animation-name:slideInLeft;animation-name:slideInLeft}@-webkit-keyframes slideInRight{0%{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes slideInRight{0%{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.slideInRight{-webkit-animation-name:slideInRight;animation-name:slideInRight}@-webkit-keyframes slideInUp{0%{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes slideInUp{0%{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.slideInUp{-webkit-animation-name:slideInUp;animation-name:slideInUp}@-webkit-keyframes slideOutDown{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes slideOutDown{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.slideOutDown{-webkit-animation-name:slideOutDown;animation-name:slideOutDown}@-webkit-keyframes slideOutLeft{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes slideOutLeft{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.slideOutLeft{-webkit-animation-name:slideOutLeft;animation-name:slideOutLeft}@-webkit-keyframes slideOutRight{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes slideOutRight{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.slideOutRight{-webkit-animation-name:slideOutRight;animation-name:slideOutRight}@-webkit-keyframes slideOutUp{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes slideOutUp{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.slideOutUp{-webkit-animation-name:slideOutUp;animation-name:slideOutUp} 12 | -------------------------------------------------------------------------------- /src/styles/_global.scss: -------------------------------------------------------------------------------- 1 | ::selection { 2 | background: $brand-primary; 3 | color: #fff; 4 | text-shadow: 0px 0px 0px; 5 | } 6 | 7 | ::-moz-selection { 8 | background: $brand-primary; 9 | color: #fff; 10 | text-shadow: 0px 0px 0px; 11 | } 12 | 13 | ::-webkit-selection { 14 | background: $brand-primary; 15 | color: #fff; 16 | text-shadow: 0px 0px 0px; 17 | } 18 | 19 | html { 20 | -webkit-font-smoothing: antialiased; 21 | -moz-osx-font-smoothing: grayscale; 22 | height: 100%; 23 | } 24 | 25 | body { 26 | padding-top: 5em; 27 | } 28 | -------------------------------------------------------------------------------- /src/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | // Table of Contents 2 | // 3 | // Colors 4 | // Options 5 | // Spacing 6 | // Body 7 | // Links 8 | // Grid breakpoints 9 | // Grid containers 10 | // Grid columns 11 | // Fonts 12 | // Components 13 | // Tables 14 | // Buttons 15 | // Forms 16 | // Dropdowns 17 | // Z-index master list 18 | // Navbar 19 | // Navs 20 | // Pagination 21 | // Jumbotron 22 | // Form states and alerts 23 | // Cards 24 | // Tooltips 25 | // Popovers 26 | // Badges 27 | // Modals 28 | // Alerts 29 | // Progress bars 30 | // List group 31 | // Image thumbnails 32 | // Figures 33 | // Breadcrumbs 34 | // Carousel 35 | // Close 36 | // Code 37 | 38 | @mixin _assert-ascending($map, $map-name) { 39 | $prev-key: null; 40 | $prev-num: null; 41 | @each $key, $num in $map { 42 | @if $prev-num == null { 43 | // Do nothing 44 | } @else if not comparable($prev-num, $num) { 45 | @warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !"; 46 | } @else if $prev-num >= $num { 47 | @warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !"; 48 | } 49 | $prev-key: $key; 50 | $prev-num: $num; 51 | } 52 | } 53 | 54 | // Replace `$search` with `$replace` in `$string` 55 | // @author Hugo Giraudel 56 | // @param {String} $string - Initial string 57 | // @param {String} $search - Substring to replace 58 | // @param {String} $replace ('') - New value 59 | // @return {String} - Updated string 60 | @function str-replace($string, $search, $replace: "") { 61 | $index: str-index($string, $search); 62 | 63 | @if $index { 64 | @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); 65 | } 66 | 67 | @return $string; 68 | } 69 | 70 | @mixin _assert-starts-at-zero($map) { 71 | $values: map-values($map); 72 | $first-value: nth($values, 1); 73 | @if $first-value != 0 { 74 | @warn "First breakpoint in `$grid-breakpoints` must start at 0, but starts at #{$first-value}."; 75 | } 76 | } 77 | 78 | 79 | // General variable structure 80 | // 81 | // Variable format should follow the `$component-modifier-state-property` order. 82 | 83 | $fa-font-path: "~font-awesome/fonts"; 84 | 85 | // Colors 86 | // 87 | // Grayscale and brand colors for use across Bootstrap. 88 | 89 | // Start with assigning color names to specific hex values. 90 | $white: #fff !default; 91 | $black: #000 !default; 92 | $red: #d9534f !default; 93 | $orange: #f0ad4e !default; 94 | $yellow: #ffd500 !default; 95 | $green: #5cb85c !default; 96 | $blue: #0275d8 !default; 97 | $teal: #5bc0de !default; 98 | $pink: #ff5b77 !default; 99 | $purple: #613d7c !default; 100 | 101 | // Create grayscale 102 | $gray-dark: #292b2c !default; 103 | $gray: #464a4c !default; 104 | $gray-light: #636c72 !default; 105 | $gray-lighter: #eceeef !default; 106 | $gray-lightest: #f7f7f9 !default; 107 | 108 | // Reassign color vars to semantic color scheme 109 | $brand-primary: $blue !default; 110 | $brand-success: $green !default; 111 | $brand-info: $teal !default; 112 | $brand-warning: $orange !default; 113 | $brand-danger: $red !default; 114 | $brand-inverse: $gray-dark !default; 115 | 116 | 117 | // Options 118 | // 119 | // Quickly modify global styling by enabling or disabling optional features. 120 | 121 | $enable-rounded: true !default; 122 | $enable-shadows: false !default; 123 | $enable-gradients: false !default; 124 | $enable-transitions: true !default; 125 | $enable-hover-media-query: false !default; 126 | $enable-grid-classes: true !default; 127 | $enable-print-styles: true !default; 128 | 129 | 130 | // Spacing 131 | // 132 | // Control the default styling of most Bootstrap elements by modifying these 133 | // variables. Mostly focused on spacing. 134 | // You can add more entries to the $spacers map, should you need more variation. 135 | 136 | $spacer: 1rem !default; 137 | $spacer-x: $spacer !default; 138 | $spacer-y: $spacer !default; 139 | $spacers: ( 140 | 0: ( 141 | x: 0, 142 | y: 0 143 | ), 144 | 1: ( 145 | x: ($spacer-x * .25), 146 | y: ($spacer-y * .25) 147 | ), 148 | 2: ( 149 | x: ($spacer-x * .5), 150 | y: ($spacer-y * .5) 151 | ), 152 | 3: ( 153 | x: $spacer-x, 154 | y: $spacer-y 155 | ), 156 | 4: ( 157 | x: ($spacer-x * 1.5), 158 | y: ($spacer-y * 1.5) 159 | ), 160 | 5: ( 161 | x: ($spacer-x * 3), 162 | y: ($spacer-y * 3) 163 | ) 164 | ) !default; 165 | $border-width: 1px !default; 166 | 167 | // This variable affects the `.h-*` and `.w-*` classes. 168 | $sizes: ( 169 | 25: 25%, 170 | 50: 50%, 171 | 75: 75%, 172 | 100: 100% 173 | ) !default; 174 | 175 | // Body 176 | // 177 | // Settings for the `` element. 178 | 179 | $body-bg: $white !default; 180 | $body-color: $gray-dark !default; 181 | $inverse-bg: $gray-dark !default; 182 | $inverse-color: $gray-lighter !default; 183 | 184 | 185 | // Links 186 | // 187 | // Style anchor elements. 188 | 189 | $link-color: $brand-primary !default; 190 | $link-decoration: none !default; 191 | $link-hover-color: darken($link-color, 15%) !default; 192 | $link-hover-decoration: underline !default; 193 | 194 | 195 | // Grid breakpoints 196 | // 197 | // Define the minimum dimensions at which your layout will change, 198 | // adapting to different screen sizes, for use in media queries. 199 | 200 | $grid-breakpoints: ( 201 | xs: 0, 202 | sm: 576px, 203 | md: 768px, 204 | lg: 992px, 205 | xl: 1200px 206 | ) !default; 207 | @include _assert-ascending($grid-breakpoints, "$grid-breakpoints"); 208 | @include _assert-starts-at-zero($grid-breakpoints); 209 | 210 | 211 | // Grid containers 212 | // 213 | // Define the maximum width of `.container` for different screen sizes. 214 | 215 | $container-max-widths: ( 216 | sm: 540px, 217 | md: 720px, 218 | lg: 960px, 219 | xl: 1140px 220 | ) !default; 221 | @include _assert-ascending($container-max-widths, "$container-max-widths"); 222 | 223 | 224 | // Grid columns 225 | // 226 | // Set the number of columns and specify the width of the gutters. 227 | 228 | $grid-columns: 12 !default; 229 | $grid-gutter-width-base: 30px !default; 230 | $grid-gutter-widths: ( 231 | xs: $grid-gutter-width-base, 232 | sm: $grid-gutter-width-base, 233 | md: $grid-gutter-width-base, 234 | lg: $grid-gutter-width-base, 235 | xl: $grid-gutter-width-base 236 | ) !default; 237 | 238 | // Fonts 239 | // 240 | // Font, line-height, and color for body text, headings, and more. 241 | 242 | $font-family-sans-serif: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !default; 243 | $font-family-serif: Georgia, "Times New Roman", Times, serif !default; 244 | $font-family-monospace: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default; 245 | $font-family-base: $font-family-sans-serif !default; 246 | 247 | $font-size-base: 1rem !default; // Assumes the browser default, typically `16px` 248 | $font-size-lg: 1.25rem !default; 249 | $font-size-sm: .875rem !default; 250 | $font-size-xs: .75rem !default; 251 | 252 | $font-weight-normal: normal !default; 253 | $font-weight-bold: bold !default; 254 | 255 | $font-weight-base: $font-weight-normal !default; 256 | $line-height-base: 1.5 !default; 257 | 258 | $font-size-h1: 2.5rem !default; 259 | $font-size-h2: 2rem !default; 260 | $font-size-h3: 1.75rem !default; 261 | $font-size-h4: 1.5rem !default; 262 | $font-size-h5: 1.25rem !default; 263 | $font-size-h6: 1rem !default; 264 | 265 | $headings-margin-bottom: ($spacer / 2) !default; 266 | $headings-font-family: inherit !default; 267 | $headings-font-weight: 500 !default; 268 | $headings-line-height: 1.1 !default; 269 | $headings-color: inherit !default; 270 | 271 | $display1-size: 6rem !default; 272 | $display2-size: 5.5rem !default; 273 | $display3-size: 4.5rem !default; 274 | $display4-size: 3.5rem !default; 275 | 276 | $display1-weight: 300 !default; 277 | $display2-weight: 300 !default; 278 | $display3-weight: 300 !default; 279 | $display4-weight: 300 !default; 280 | $display-line-height: $headings-line-height !default; 281 | 282 | $lead-font-size: 1.25rem !default; 283 | $lead-font-weight: 300 !default; 284 | 285 | $small-font-size: 80% !default; 286 | 287 | $text-muted: $gray-light !default; 288 | 289 | $abbr-border-color: $gray-light !default; 290 | 291 | $blockquote-small-color: $gray-light !default; 292 | $blockquote-font-size: ($font-size-base * 1.25) !default; 293 | $blockquote-border-color: $gray-lighter !default; 294 | $blockquote-border-width: .25rem !default; 295 | 296 | $hr-border-color: rgba($black,.1) !default; 297 | $hr-border-width: $border-width !default; 298 | 299 | $mark-padding: .2em !default; 300 | 301 | $dt-font-weight: $font-weight-bold !default; 302 | 303 | $kbd-box-shadow: inset 0 -.1rem 0 rgba($black,.25) !default; 304 | $nested-kbd-font-weight: $font-weight-bold !default; 305 | 306 | $list-inline-padding: 5px !default; 307 | 308 | 309 | // Components 310 | // 311 | // Define common padding and border radius sizes and more. 312 | 313 | $line-height-lg: (4 / 3) !default; 314 | $line-height-sm: 1.5 !default; 315 | 316 | $border-radius: .25rem !default; 317 | $border-radius-lg: .3rem !default; 318 | $border-radius-sm: .2rem !default; 319 | 320 | $component-active-color: $white !default; 321 | $component-active-bg: $brand-primary !default; 322 | 323 | $caret-width: .3em !default; 324 | 325 | $transition-base: all .2s ease-in-out !default; 326 | $transition-fade: opacity .15s linear !default; 327 | $transition-collapse: height .35s ease !default; 328 | 329 | 330 | // Tables 331 | // 332 | // Customizes the `.table` component with basic values, each used across all table variations. 333 | 334 | $table-cell-padding: .75rem !default; 335 | $table-sm-cell-padding: .3rem !default; 336 | 337 | $table-bg: transparent !default; 338 | 339 | $table-inverse-bg: $gray-dark !default; 340 | $table-inverse-color: $body-bg !default; 341 | 342 | $table-bg-accent: rgba($black,.05) !default; 343 | $table-bg-hover: rgba($black,.075) !default; 344 | $table-bg-active: $table-bg-hover !default; 345 | 346 | $table-head-bg: $gray-lighter !default; 347 | $table-head-color: $gray !default; 348 | 349 | $table-border-width: $border-width !default; 350 | $table-border-color: $gray-lighter !default; 351 | 352 | 353 | // Buttons 354 | // 355 | // For each of Bootstrap's buttons, define text, background and border color. 356 | 357 | $btn-padding-x: 1rem !default; 358 | $btn-padding-y: .5rem !default; 359 | $btn-line-height: 1.25 !default; 360 | $btn-font-weight: $font-weight-normal !default; 361 | $btn-box-shadow: inset 0 1px 0 rgba($white,.15), 0 1px 1px rgba($black,.075) !default; 362 | $btn-focus-box-shadow: 0 0 0 2px rgba($brand-primary, .25) !default; 363 | $btn-active-box-shadow: inset 0 3px 5px rgba($black,.125) !default; 364 | 365 | $btn-primary-color: $white !default; 366 | $btn-primary-bg: $brand-primary !default; 367 | $btn-primary-border: $btn-primary-bg !default; 368 | 369 | $btn-secondary-color: $gray-dark !default; 370 | $btn-secondary-bg: $white !default; 371 | $btn-secondary-border: #ccc !default; 372 | 373 | $btn-info-color: $white !default; 374 | $btn-info-bg: $brand-info !default; 375 | $btn-info-border: $btn-info-bg !default; 376 | 377 | $btn-success-color: $white !default; 378 | $btn-success-bg: $brand-success !default; 379 | $btn-success-border: $btn-success-bg !default; 380 | 381 | $btn-warning-color: $white !default; 382 | $btn-warning-bg: $brand-warning !default; 383 | $btn-warning-border: $btn-warning-bg !default; 384 | 385 | $btn-danger-color: $white !default; 386 | $btn-danger-bg: $brand-danger !default; 387 | $btn-danger-border: $btn-danger-bg !default; 388 | 389 | $btn-link-disabled-color: $gray-light !default; 390 | 391 | $btn-padding-x-sm: .5rem !default; 392 | $btn-padding-y-sm: .25rem !default; 393 | 394 | $btn-padding-x-lg: 1.5rem !default; 395 | $btn-padding-y-lg: .75rem !default; 396 | 397 | $btn-block-spacing-y: .5rem !default; 398 | $btn-toolbar-margin: .5rem !default; 399 | 400 | // Allows for customizing button radius independently from global border radius 401 | $btn-border-radius: $border-radius !default; 402 | $btn-border-radius-lg: $border-radius-lg !default; 403 | $btn-border-radius-sm: $border-radius-sm !default; 404 | 405 | $btn-transition: all .2s ease-in-out !default; 406 | 407 | 408 | // Forms 409 | 410 | $input-padding-x: .75rem !default; 411 | $input-padding-y: .5rem !default; 412 | $input-line-height: 1.25 !default; 413 | 414 | $input-bg: $white !default; 415 | $input-bg-disabled: $gray-lighter !default; 416 | 417 | $input-color: $gray !default; 418 | $input-border-color: rgba($black,.15) !default; 419 | $input-btn-border-width: $border-width !default; // For form controls and buttons 420 | $input-box-shadow: inset 0 1px 1px rgba($black,.075) !default; 421 | 422 | $input-border-radius: $border-radius !default; 423 | $input-border-radius-lg: $border-radius-lg !default; 424 | $input-border-radius-sm: $border-radius-sm !default; 425 | 426 | $input-bg-focus: $input-bg !default; 427 | $input-border-focus: lighten($brand-primary, 25%) !default; 428 | $input-box-shadow-focus: $input-box-shadow, rgba($input-border-focus, .6) !default; 429 | $input-color-focus: $input-color !default; 430 | 431 | $input-color-placeholder: $gray-light !default; 432 | 433 | $input-padding-x-sm: .5rem !default; 434 | $input-padding-y-sm: .25rem !default; 435 | 436 | $input-padding-x-lg: 1.5rem !default; 437 | $input-padding-y-lg: .75rem !default; 438 | 439 | $input-height: (($font-size-base * $input-line-height) + ($input-padding-y * 2)) !default; 440 | $input-height-lg: (($font-size-lg * $line-height-lg) + ($input-padding-y-lg * 2)) !default; 441 | $input-height-sm: (($font-size-sm * $line-height-sm) + ($input-padding-y-sm * 2)) !default; 442 | 443 | $input-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s !default; 444 | 445 | $form-text-margin-top: .25rem !default; 446 | $form-feedback-margin-top: $form-text-margin-top !default; 447 | 448 | $form-check-margin-bottom: .5rem !default; 449 | $form-check-input-gutter: 1.25rem !default; 450 | $form-check-input-margin-y: .25rem !default; 451 | $form-check-input-margin-x: .25rem !default; 452 | 453 | $form-check-inline-margin-x: .75rem !default; 454 | 455 | $form-group-margin-bottom: $spacer-y !default; 456 | 457 | $input-group-addon-bg: $gray-lighter !default; 458 | $input-group-addon-border-color: $input-border-color !default; 459 | 460 | $cursor-disabled: not-allowed !default; 461 | 462 | $custom-control-gutter: 1.5rem !default; 463 | $custom-control-spacer-x: 1rem !default; 464 | $custom-control-spacer-y: .25rem !default; 465 | 466 | $custom-control-indicator-size: 1rem !default; 467 | $custom-control-indicator-margin-y: (($line-height-base * 1rem) - $custom-control-indicator-size) / -2 !default; 468 | $custom-control-indicator-bg: #ddd !default; 469 | $custom-control-indicator-bg-size: 50% 50% !default; 470 | $custom-control-indicator-box-shadow: inset 0 .25rem .25rem rgba($black,.1) !default; 471 | 472 | $custom-control-disabled-cursor: $cursor-disabled !default; 473 | $custom-control-disabled-indicator-bg: $gray-lighter !default; 474 | $custom-control-disabled-description-color: $gray-light !default; 475 | 476 | $custom-control-checked-indicator-color: $white !default; 477 | $custom-control-checked-indicator-bg: $brand-primary !default; 478 | $custom-control-checked-indicator-box-shadow: none !default; 479 | 480 | $custom-control-focus-indicator-box-shadow: 0 0 0 1px $body-bg, 0 0 0 3px $brand-primary !default; 481 | 482 | $custom-control-active-indicator-color: $white !default; 483 | $custom-control-active-indicator-bg: lighten($brand-primary, 35%) !default; 484 | $custom-control-active-indicator-box-shadow: none !default; 485 | 486 | $custom-checkbox-radius: $border-radius !default; 487 | $custom-checkbox-checked-icon: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='#{$custom-control-checked-indicator-color}' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"), "#", "%23") !default; 488 | 489 | $custom-checkbox-indeterminate-bg: $brand-primary !default; 490 | $custom-checkbox-indeterminate-indicator-color: $custom-control-checked-indicator-color !default; 491 | $custom-checkbox-indeterminate-icon: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='#{$custom-checkbox-indeterminate-indicator-color}' d='M0 2h4'/%3E%3C/svg%3E"), "#", "%23") !default; 492 | $custom-checkbox-indeterminate-box-shadow: none !default; 493 | 494 | $custom-radio-radius: 50% !default; 495 | $custom-radio-checked-icon: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='#{$custom-control-checked-indicator-color}'/%3E%3C/svg%3E"), "#", "%23") !default; 496 | 497 | $custom-select-padding-x: .75rem !default; 498 | $custom-select-padding-y: .375rem !default; 499 | $custom-select-indicator-padding: 1rem !default; // Extra padding to account for the presence of the background-image based indicator 500 | $custom-select-line-height: $input-line-height !default; 501 | $custom-select-color: $input-color !default; 502 | $custom-select-disabled-color: $gray-light !default; 503 | $custom-select-bg: $white !default; 504 | $custom-select-disabled-bg: $gray-lighter !default; 505 | $custom-select-bg-size: 8px 10px !default; // In pixels because image dimensions 506 | $custom-select-indicator-color: #333 !default; 507 | $custom-select-indicator: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='#{$custom-select-indicator-color}' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E"), "#", "%23") !default; 508 | $custom-select-border-width: $input-btn-border-width !default; 509 | $custom-select-border-color: $input-border-color !default; 510 | $custom-select-border-radius: $border-radius !default; 511 | 512 | $custom-select-focus-border-color: lighten($brand-primary, 25%) !default; 513 | $custom-select-focus-box-shadow: inset 0 1px 2px rgba($black, .075), 0 0 5px rgba($custom-select-focus-border-color, .5) !default; 514 | 515 | $custom-select-sm-padding-y: .2rem !default; 516 | $custom-select-sm-font-size: 75% !default; 517 | 518 | $custom-file-height: 2.5rem !default; 519 | $custom-file-width: 14rem !default; 520 | $custom-file-focus-box-shadow: 0 0 0 .075rem $white, 0 0 0 .2rem $brand-primary !default; 521 | 522 | $custom-file-padding-x: .5rem !default; 523 | $custom-file-padding-y: 1rem !default; 524 | $custom-file-line-height: 1.5 !default; 525 | $custom-file-color: $gray !default; 526 | $custom-file-bg: $white !default; 527 | $custom-file-border-width: $border-width !default; 528 | $custom-file-border-color: $input-border-color !default; 529 | $custom-file-border-radius: $border-radius !default; 530 | $custom-file-box-shadow: inset 0 .2rem .4rem rgba($black,.05) !default; 531 | $custom-file-button-color: $custom-file-color !default; 532 | $custom-file-button-bg: $gray-lighter !default; 533 | $custom-file-text: ( 534 | placeholder: ( 535 | en: "Choose file..." 536 | ), 537 | button-label: ( 538 | en: "Browse" 539 | ) 540 | ) !default; 541 | 542 | 543 | // Form validation icons 544 | $form-icon-success-color: $brand-success !default; 545 | $form-icon-success: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='#{$form-icon-success-color}' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E"), "#", "%23") !default; 546 | 547 | $form-icon-warning-color: $brand-warning !default; 548 | $form-icon-warning: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='#{$form-icon-warning-color}' d='M4.4 5.324h-.8v-2.46h.8zm0 1.42h-.8V5.89h.8zM3.76.63L.04 7.075c-.115.2.016.425.26.426h7.397c.242 0 .372-.226.258-.426C6.726 4.924 5.47 2.79 4.253.63c-.113-.174-.39-.174-.494 0z'/%3E%3C/svg%3E"), "#", "%23") !default; 549 | 550 | $form-icon-danger-color: $brand-danger !default; 551 | $form-icon-danger: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$form-icon-danger-color}' viewBox='-2 -2 7 7'%3E%3Cpath stroke='%23d9534f' d='M0 0l3 3m0-3L0 3'/%3E%3Ccircle r='.5'/%3E%3Ccircle cx='3' r='.5'/%3E%3Ccircle cy='3' r='.5'/%3E%3Ccircle cx='3' cy='3' r='.5'/%3E%3C/svg%3E"), "#", "%23") !default; 552 | 553 | 554 | // Dropdowns 555 | // 556 | // Dropdown menu container and contents. 557 | 558 | $dropdown-min-width: 10rem !default; 559 | $dropdown-padding-y: .5rem !default; 560 | $dropdown-margin-top: .125rem !default; 561 | $dropdown-bg: $white !default; 562 | $dropdown-border-color: rgba($black,.15) !default; 563 | $dropdown-border-width: $border-width !default; 564 | $dropdown-divider-bg: $gray-lighter !default; 565 | $dropdown-box-shadow: 0 .5rem 1rem rgba($black,.175) !default; 566 | 567 | $dropdown-link-color: $gray-dark !default; 568 | $dropdown-link-hover-color: darken($gray-dark, 5%) !default; 569 | $dropdown-link-hover-bg: $gray-lightest !default; 570 | 571 | $dropdown-link-active-color: $component-active-color !default; 572 | $dropdown-link-active-bg: $component-active-bg !default; 573 | 574 | $dropdown-link-disabled-color: $gray-light !default; 575 | 576 | $dropdown-item-padding-x: 1.5rem !default; 577 | 578 | $dropdown-header-color: $gray-light !default; 579 | 580 | 581 | // Z-index master list 582 | // 583 | // Warning: Avoid customizing these values. They're used for a bird's eye view 584 | // of components dependent on the z-axis and are designed to all work together. 585 | 586 | $zindex-dropdown-backdrop: 990 !default; 587 | $zindex-navbar: 1000 !default; 588 | $zindex-dropdown: 1000 !default; 589 | $zindex-fixed: 1030 !default; 590 | $zindex-sticky: 1030 !default; 591 | $zindex-modal-backdrop: 1040 !default; 592 | $zindex-modal: 1050 !default; 593 | $zindex-popover: 1060 !default; 594 | $zindex-tooltip: 1070 !default; 595 | 596 | 597 | // Navbar 598 | 599 | $navbar-border-radius: $border-radius !default; 600 | $navbar-padding-x: $spacer !default; 601 | $navbar-padding-y: ($spacer / 2) !default; 602 | 603 | $navbar-brand-padding-y: .25rem !default; 604 | 605 | $navbar-toggler-padding-x: .75rem !default; 606 | $navbar-toggler-padding-y: .25rem !default; 607 | $navbar-toggler-font-size: $font-size-lg !default; 608 | $navbar-toggler-border-radius: $btn-border-radius !default; 609 | 610 | $navbar-inverse-color: rgba($white,.5) !default; 611 | $navbar-inverse-hover-color: rgba($white,.75) !default; 612 | $navbar-inverse-active-color: rgba($white,1) !default; 613 | $navbar-inverse-disabled-color: rgba($white,.25) !default; 614 | $navbar-inverse-toggler-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-inverse-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E"), "#", "%23") !default; 615 | $navbar-inverse-toggler-border: rgba($white,.1) !default; 616 | 617 | $navbar-light-color: rgba($black,.5) !default; 618 | $navbar-light-hover-color: rgba($black,.7) !default; 619 | $navbar-light-active-color: rgba($black,.9) !default; 620 | $navbar-light-disabled-color: rgba($black,.3) !default; 621 | $navbar-light-toggler-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-light-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E"), "#", "%23") !default; 622 | $navbar-light-toggler-border: rgba($black,.1) !default; 623 | 624 | // Navs 625 | 626 | $nav-item-margin: .2rem !default; 627 | $nav-item-inline-spacer: 1rem !default; 628 | $nav-link-padding: .5em 1em !default; 629 | $nav-link-hover-bg: $gray-lighter !default; 630 | $nav-disabled-link-color: $gray-light !default; 631 | 632 | $nav-tabs-border-color: #ddd !default; 633 | $nav-tabs-border-width: $border-width !default; 634 | $nav-tabs-border-radius: $border-radius !default; 635 | $nav-tabs-link-hover-border-color: $gray-lighter !default; 636 | $nav-tabs-active-link-hover-color: $gray !default; 637 | $nav-tabs-active-link-hover-bg: $body-bg !default; 638 | $nav-tabs-active-link-hover-border-color: #ddd !default; 639 | $nav-tabs-justified-link-border-color: #ddd !default; 640 | $nav-tabs-justified-active-link-border-color: $body-bg !default; 641 | 642 | $nav-pills-border-radius: $border-radius !default; 643 | $nav-pills-active-link-color: $component-active-color !default; 644 | $nav-pills-active-link-bg: $component-active-bg !default; 645 | 646 | 647 | // Pagination 648 | 649 | $pagination-padding-x: .75rem !default; 650 | $pagination-padding-y: .5rem !default; 651 | $pagination-padding-x-sm: .5rem !default; 652 | $pagination-padding-y-sm: .25rem !default; 653 | $pagination-padding-x-lg: 1.5rem !default; 654 | $pagination-padding-y-lg: .75rem !default; 655 | $pagination-line-height: 1.25 !default; 656 | 657 | $pagination-color: $link-color !default; 658 | $pagination-bg: $white !default; 659 | $pagination-border-width: $border-width !default; 660 | $pagination-border-color: #ddd !default; 661 | 662 | $pagination-hover-color: $link-hover-color !default; 663 | $pagination-hover-bg: $gray-lighter !default; 664 | $pagination-hover-border: #ddd !default; 665 | 666 | $pagination-active-color: $white !default; 667 | $pagination-active-bg: $brand-primary !default; 668 | $pagination-active-border: $brand-primary !default; 669 | 670 | $pagination-disabled-color: $gray-light !default; 671 | $pagination-disabled-bg: $white !default; 672 | $pagination-disabled-border: #ddd !default; 673 | 674 | 675 | // Jumbotron 676 | 677 | $jumbotron-padding: 2rem !default; 678 | $jumbotron-bg: $gray-lighter !default; 679 | 680 | 681 | // Form states and alerts 682 | // 683 | // Define colors for form feedback states and, by default, alerts. 684 | 685 | $state-success-text: #3c763d !default; 686 | $state-success-bg: #dff0d8 !default; 687 | $state-success-border: darken($state-success-bg, 5%) !default; 688 | 689 | $state-info-text: #31708f !default; 690 | $state-info-bg: #d9edf7 !default; 691 | $state-info-border: darken($state-info-bg, 7%) !default; 692 | 693 | $state-warning-text: #8a6d3b !default; 694 | $state-warning-bg: #fcf8e3 !default; 695 | $mark-bg: $state-warning-bg !default; 696 | $state-warning-border: darken($state-warning-bg, 5%) !default; 697 | 698 | $state-danger-text: #a94442 !default; 699 | $state-danger-bg: #f2dede !default; 700 | $state-danger-border: darken($state-danger-bg, 5%) !default; 701 | 702 | 703 | // Cards 704 | 705 | $card-spacer-x: 1.25rem !default; 706 | $card-spacer-y: .75rem !default; 707 | $card-border-width: 1px !default; 708 | $card-border-radius: $border-radius !default; 709 | $card-border-color: rgba($black,.125) !default; 710 | $card-border-radius-inner: calc(#{$card-border-radius} - #{$card-border-width}) !default; 711 | $card-cap-bg: $gray-lightest !default; 712 | $card-bg: $white !default; 713 | 714 | $card-link-hover-color: $white !default; 715 | 716 | $card-img-overlay-padding: 1.25rem !default; 717 | 718 | $card-deck-margin: ($grid-gutter-width-base / 2) !default; 719 | 720 | $card-columns-count: 3 !default; 721 | $card-columns-gap: 1.25rem !default; 722 | $card-columns-margin: $card-spacer-y !default; 723 | 724 | 725 | // Tooltips 726 | 727 | $tooltip-max-width: 200px !default; 728 | $tooltip-color: $white !default; 729 | $tooltip-bg: $black !default; 730 | $tooltip-opacity: .9 !default; 731 | $tooltip-padding-y: 3px !default; 732 | $tooltip-padding-x: 8px !default; 733 | $tooltip-margin: 3px !default; 734 | 735 | $tooltip-arrow-width: 5px !default; 736 | $tooltip-arrow-color: $tooltip-bg !default; 737 | 738 | 739 | // Popovers 740 | 741 | $popover-inner-padding: 1px !default; 742 | $popover-bg: $white !default; 743 | $popover-max-width: 276px !default; 744 | $popover-border-width: $border-width !default; 745 | $popover-border-color: rgba($black,.2) !default; 746 | $popover-box-shadow: 0 5px 10px rgba($black,.2) !default; 747 | 748 | $popover-title-bg: darken($popover-bg, 3%) !default; 749 | $popover-title-padding-x: 14px !default; 750 | $popover-title-padding-y: 8px !default; 751 | 752 | $popover-content-padding-x: 14px !default; 753 | $popover-content-padding-y: 9px !default; 754 | 755 | $popover-arrow-width: 10px !default; 756 | $popover-arrow-color: $popover-bg !default; 757 | 758 | $popover-arrow-outer-width: ($popover-arrow-width + 1px) !default; 759 | $popover-arrow-outer-color: fade-in($popover-border-color, .05) !default; 760 | 761 | 762 | // Badges 763 | 764 | $badge-default-bg: $gray-light !default; 765 | $badge-primary-bg: $brand-primary !default; 766 | $badge-success-bg: $brand-success !default; 767 | $badge-info-bg: $brand-info !default; 768 | $badge-warning-bg: $brand-warning !default; 769 | $badge-danger-bg: $brand-danger !default; 770 | 771 | $badge-color: $white !default; 772 | $badge-link-hover-color: $white !default; 773 | $badge-font-size: 75% !default; 774 | $badge-font-weight: $font-weight-bold !default; 775 | $badge-padding-x: .4em !default; 776 | $badge-padding-y: .25em !default; 777 | 778 | $badge-pill-padding-x: .6em !default; 779 | // Use a higher than normal value to ensure completely rounded edges when 780 | // customizing padding or font-size on labels. 781 | $badge-pill-border-radius: 10rem !default; 782 | 783 | 784 | // Modals 785 | 786 | // Padding applied to the modal body 787 | $modal-inner-padding: 15px !default; 788 | 789 | $modal-dialog-margin: 10px !default; 790 | $modal-dialog-sm-up-margin-y: 30px !default; 791 | 792 | $modal-title-line-height: $line-height-base !default; 793 | 794 | $modal-content-bg: $white !default; 795 | $modal-content-border-color: rgba($black,.2) !default; 796 | $modal-content-border-width: $border-width !default; 797 | $modal-content-xs-box-shadow: 0 3px 9px rgba($black,.5) !default; 798 | $modal-content-sm-up-box-shadow: 0 5px 15px rgba($black,.5) !default; 799 | 800 | $modal-backdrop-bg: $black !default; 801 | $modal-backdrop-opacity: .5 !default; 802 | $modal-header-border-color: $gray-lighter !default; 803 | $modal-footer-border-color: $modal-header-border-color !default; 804 | $modal-header-border-width: $modal-content-border-width !default; 805 | $modal-footer-border-width: $modal-header-border-width !default; 806 | $modal-header-padding: 15px !default; 807 | 808 | $modal-lg: 800px !default; 809 | $modal-md: 500px !default; 810 | $modal-sm: 300px !default; 811 | 812 | $modal-transition: transform .3s ease-out !default; 813 | 814 | 815 | // Alerts 816 | // 817 | // Define alert colors, border radius, and padding. 818 | 819 | $alert-padding-x: 1.25rem !default; 820 | $alert-padding-y: .75rem !default; 821 | $alert-margin-bottom: $spacer-y !default; 822 | $alert-border-radius: $border-radius !default; 823 | $alert-link-font-weight: $font-weight-bold !default; 824 | $alert-border-width: $border-width !default; 825 | 826 | $alert-success-bg: $state-success-bg !default; 827 | $alert-success-text: $state-success-text !default; 828 | $alert-success-border: $state-success-border !default; 829 | 830 | $alert-info-bg: $state-info-bg !default; 831 | $alert-info-text: $state-info-text !default; 832 | $alert-info-border: $state-info-border !default; 833 | 834 | $alert-warning-bg: $state-warning-bg !default; 835 | $alert-warning-text: $state-warning-text !default; 836 | $alert-warning-border: $state-warning-border !default; 837 | 838 | $alert-danger-bg: $state-danger-bg !default; 839 | $alert-danger-text: $state-danger-text !default; 840 | $alert-danger-border: $state-danger-border !default; 841 | 842 | 843 | // Progress bars 844 | 845 | $progress-height: 1rem !default; 846 | $progress-font-size: .75rem !default; 847 | $progress-bg: $gray-lighter !default; 848 | $progress-border-radius: $border-radius !default; 849 | $progress-box-shadow: inset 0 .1rem .1rem rgba($black,.1) !default; 850 | $progress-bar-color: $white !default; 851 | $progress-bar-bg: $brand-primary !default; 852 | $progress-bar-animation-timing: 1s linear infinite !default; 853 | 854 | // List group 855 | 856 | $list-group-color: $body-color !default; 857 | $list-group-bg: $white !default; 858 | $list-group-border-color: rgba($black,.125) !default; 859 | $list-group-border-width: $border-width !default; 860 | $list-group-border-radius: $border-radius !default; 861 | 862 | $list-group-item-padding-x: 1.25rem !default; 863 | $list-group-item-padding-y: .75rem !default; 864 | 865 | $list-group-hover-bg: $gray-lightest !default; 866 | $list-group-active-color: $component-active-color !default; 867 | $list-group-active-bg: $component-active-bg !default; 868 | $list-group-active-border: $list-group-active-bg !default; 869 | $list-group-active-text-color: lighten($list-group-active-bg, 50%) !default; 870 | 871 | $list-group-disabled-color: $gray-light !default; 872 | $list-group-disabled-bg: $list-group-bg !default; 873 | $list-group-disabled-text-color: $list-group-disabled-color !default; 874 | 875 | $list-group-link-color: $gray !default; 876 | $list-group-link-heading-color: $gray-dark !default; 877 | $list-group-link-hover-color: $list-group-link-color !default; 878 | 879 | $list-group-link-active-color: $list-group-color !default; 880 | $list-group-link-active-bg: $gray-lighter !default; 881 | 882 | 883 | // Image thumbnails 884 | 885 | $thumbnail-padding: .25rem !default; 886 | $thumbnail-bg: $body-bg !default; 887 | $thumbnail-border-width: $border-width !default; 888 | $thumbnail-border-color: #ddd !default; 889 | $thumbnail-border-radius: $border-radius !default; 890 | $thumbnail-box-shadow: 0 1px 2px rgba($black,.075) !default; 891 | $thumbnail-transition: all .2s ease-in-out !default; 892 | 893 | 894 | // Figures 895 | 896 | $figure-caption-font-size: 90% !default; 897 | $figure-caption-color: $gray-light !default; 898 | 899 | 900 | // Breadcrumbs 901 | 902 | $breadcrumb-padding-y: .75rem !default; 903 | $breadcrumb-padding-x: 1rem !default; 904 | $breadcrumb-item-padding: .5rem !default; 905 | 906 | $breadcrumb-bg: $gray-lighter !default; 907 | $breadcrumb-divider-color: $gray-light !default; 908 | $breadcrumb-active-color: $gray-light !default; 909 | $breadcrumb-divider: "/" !default; 910 | 911 | 912 | // Carousel 913 | 914 | $carousel-control-color: $white !default; 915 | $carousel-control-width: 15% !default; 916 | $carousel-control-opacity: .5 !default; 917 | 918 | $carousel-indicator-width: 30px !default; 919 | $carousel-indicator-height: 3px !default; 920 | $carousel-indicator-spacer: 3px !default; 921 | $carousel-indicator-active-bg: $white !default; 922 | 923 | $carousel-caption-width: 70% !default; 924 | $carousel-caption-color: $white !default; 925 | 926 | $carousel-control-icon-width: 20px !default; 927 | 928 | $carousel-control-prev-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M4 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E"), "#", "%23") !default; 929 | $carousel-control-next-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M1.5 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E"), "#", "%23") !default; 930 | 931 | $carousel-transition: transform .6s ease-in-out !default; 932 | 933 | 934 | // Close 935 | 936 | $close-font-size: $font-size-base * 1.5 !default; 937 | $close-font-weight: $font-weight-bold !default; 938 | $close-color: $black !default; 939 | $close-text-shadow: 0 1px 0 $white !default; 940 | 941 | 942 | // Code 943 | 944 | $code-font-size: 90% !default; 945 | $code-padding-x: .4rem !default; 946 | $code-padding-y: .2rem !default; 947 | $code-color: #bd4147 !default; 948 | $code-bg: $gray-lightest !default; 949 | 950 | $kbd-color: $white !default; 951 | $kbd-bg: $gray-dark !default; 952 | 953 | $pre-bg: $gray-lightest !default; 954 | $pre-color: $gray-dark !default; 955 | $pre-border-color: #ccc !default; 956 | $pre-scrollable-max-height: 340px !default; 957 | -------------------------------------------------------------------------------- /src/util/helpers.js: -------------------------------------------------------------------------------- 1 | import { LoadingState } from 'src/config/loading-state'; 2 | 3 | // Helper to set global loading state 4 | export const setLoading = (isLoading = false) => LoadingState.$emit('toggle', isLoading); 5 | -------------------------------------------------------------------------------- /src/util/resources.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | import { API_BASE } from 'src/config/constants'; 4 | 5 | // Resources for /posts endpoint on API 6 | // @see https://github.com/mzabriskie/axios#creating-an-instance 7 | export const postsResource = axios.create({ 8 | baseURL: `${API_BASE}/posts/` 9 | }); 10 | -------------------------------------------------------------------------------- /webpack/development.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | 3 | const webpackBase = require('./webpack.base.js'); 4 | const plugins = require('./plugins'); 5 | 6 | module.exports = merge(webpackBase, { 7 | devtool: 'inline-source-map', 8 | plugins: plugins.develop 9 | }); 10 | -------------------------------------------------------------------------------- /webpack/plugins.js: -------------------------------------------------------------------------------- 1 | const argv = require('minimist')(process.argv.slice(2)); 2 | const BrowserSyncPlugin = require('browser-sync-webpack-plugin'); 3 | const DashboardPlugin = require('webpack-dashboard/plugin'); 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 5 | const FaviconsWebpackPlugin = require('favicons-webpack-plugin'); 6 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 7 | const imageminMozjpeg = require('imagemin-mozjpeg'); 8 | const ImageminPlugin = require('imagemin-webpack-plugin').default; 9 | const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 10 | const path = require('path'); 11 | const webpack = require('webpack'); 12 | const WebpackCleanupPlugin = require('webpack-cleanup-plugin'); 13 | const WebpackNotifierPlugin = require('webpack-notifier'); 14 | 15 | const pkg = require('../package.json'); 16 | 17 | const isProduction = !!((argv.env && argv.env.production) || argv.p); 18 | 19 | /** 20 | * Common plugins 21 | * @type {*[]} 22 | */ 23 | const commonPlugins = [ 24 | new HtmlWebpackPlugin({ 25 | inject: 'body', 26 | template: path.resolve(__dirname, '../src/index.html'), 27 | favicon: path.resolve(__dirname, '../src/assets/images/favicon.ico') 28 | }), 29 | new webpack.LoaderOptionsPlugin({ 30 | minimize: isProduction, 31 | debug: !isProduction, 32 | stats: {colors: true}, 33 | eslint: { 34 | configFile: path.resolve(__dirname, '../.eslintrc'), 35 | failOnWarning: false, 36 | failOnError: true, 37 | }, 38 | }), 39 | new ImageminPlugin({ 40 | disable: false, 41 | optipng: { 42 | optimizationLevel: 7, 43 | }, 44 | gifsicle: { 45 | optimizationLevel: 3, 46 | }, 47 | pngquant: { 48 | quality: '65-90', 49 | speed: 4, 50 | }, 51 | svgo: { 52 | removeUnknownsAndDefaults: false, 53 | cleanupIDs: false, 54 | }, 55 | jpegtran: null, 56 | plugins: [imageminMozjpeg({ 57 | quality: 75, 58 | })] 59 | }), 60 | new ExtractTextPlugin({ 61 | filename: 'styles/[name].[hash].css', 62 | allChunks: true, 63 | }), 64 | ]; 65 | 66 | /** 67 | * Develop plugins 68 | * @type {Array.<*>} 69 | */ 70 | const developPlugins = [ 71 | new DashboardPlugin(), 72 | new webpack.NamedModulesPlugin(), 73 | new WebpackNotifierPlugin({ 74 | title: pkg.name, 75 | contentImage: path.join(__dirname, '../src/assets/images/logo.png') 76 | }), 77 | new BrowserSyncPlugin( 78 | { 79 | host: 'localhost', 80 | port: pkg.config.port, 81 | // Proxy the default webpack dev-server port 82 | proxy: 'http://localhost:8080/', 83 | notify: false, 84 | open: false, 85 | // Let webpack handle the reload 86 | codeSync: false 87 | } 88 | ), 89 | new webpack.optimize.OccurrenceOrderPlugin(), 90 | new webpack.NoEmitOnErrorsPlugin() 91 | ]; 92 | 93 | /** 94 | * Production plugins 95 | * @type {Array.<*>} 96 | */ 97 | const productionPLugins = [ 98 | new webpack.DefinePlugin({ 99 | 'process.env': { 100 | NODE_ENV: '"production"' 101 | } 102 | }), 103 | new WebpackCleanupPlugin(), 104 | new webpack.optimize.OccurrenceOrderPlugin(), 105 | new webpack.optimize.UglifyJsPlugin({ 106 | compressor: { 107 | drop_console: true, 108 | warnings: false 109 | } 110 | }), 111 | new OptimizeCssAssetsPlugin({ 112 | cssProcessorOptions: { 113 | discardComments: { 114 | removeAll: true 115 | } 116 | } 117 | }), 118 | new FaviconsWebpackPlugin({ 119 | title: `${pkg.name} - ${pkg.description}`, 120 | logo: path.resolve(__dirname, '../src/assets/images/logo.png'), 121 | prefix: 'assets/img/icons/', 122 | statsFilename: 'iconstats-[hash].json', 123 | icons: { 124 | android: true, // Create Android homescreen icon. `boolean` 125 | appleIcon: true, // Create Apple touch icons. `boolean` or `{ offset: offsetInPercentage }` 126 | appleStartup: false, // Create Apple startup images. `boolean` 127 | coast: {offset: 25}, // Create Opera Coast icon with offset 25%. `boolean` or `{ offset: offsetInPercentage }` 128 | favicons: true, // Create regular favicons. `boolean` 129 | firefox: true, // Create Firefox OS icons. `boolean` or `{ offset: offsetInPercentage }` 130 | windows: true, // Create Windows 8 tile icons. `boolean` 131 | yandex: true // Create Yandex browser icon. `boolean` 132 | } 133 | }) 134 | ]; 135 | 136 | module.exports = { 137 | develop: commonPlugins.concat(developPlugins), 138 | production: commonPlugins.concat(productionPLugins) 139 | }; 140 | -------------------------------------------------------------------------------- /webpack/production.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | 3 | const webpackBase = require('./webpack.base.js'); 4 | const plugins = require('./plugins'); 5 | 6 | module.exports = merge(webpackBase, { 7 | plugins: plugins.production, 8 | performance: { 9 | hints: 'warning' 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /webpack/webpack.base.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const argv = require('minimist')(process.argv.slice(2)); 3 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 4 | 5 | const isProduction = !!((argv.env && argv.env.production) || argv.p); 6 | 7 | module.exports = { 8 | entry: { 9 | app: [path.resolve(__dirname, '../src/main.js')] 10 | }, 11 | output: { 12 | chunkFilename: '[id].chunk.js', 13 | filename: 'js/[name].[hash].js', 14 | path: path.resolve(__dirname, '../build'), 15 | publicPath: '/', 16 | sourceMapFilename: '[name].[hash].js.map', 17 | // pathinfo: true 18 | }, 19 | resolve: { 20 | alias: { 21 | 'assets': path.resolve(__dirname, '../src/assets'), 22 | 'components': path.resolve(__dirname, '../src/components'), 23 | 'src': path.resolve(__dirname, '../src'), 24 | 'vue$': 'vue/dist/vue.js' 25 | }, 26 | extensions: ['*', '.js', '.vue', 'html'], 27 | }, 28 | devServer: { 29 | historyApiFallback: true, 30 | inline: true, 31 | contentBase: path.resolve(__dirname, '../src'), 32 | compress: true 33 | }, 34 | module: { 35 | rules: [ 36 | { 37 | enforce: 'pre', 38 | exclude: /node_modules/, 39 | loader: 'eslint-loader', 40 | test: /\.js?$/ 41 | }, 42 | { 43 | exclude: [/(node_modules)(?![/|\\](bootstrap|foundation-sites))/], 44 | test: /\.js$/, 45 | use: [{ 46 | loader: 'babel-loader', 47 | query: { 48 | cacheDirectory: true 49 | } 50 | }] 51 | }, 52 | { 53 | exclude: /node_modules/, 54 | loader: 'vue-loader', 55 | test: /\.vue$/ 56 | }, 57 | { 58 | exclude: /node_modules/, 59 | loader: 'html-loader', 60 | test: /\.html$/ 61 | }, 62 | { 63 | test: /\.css$/, 64 | loader: ['css-hot-loader'].concat(ExtractTextPlugin.extract({ 65 | fallback: 'style-loader', 66 | use: [ 67 | { 68 | loader: 'cache-loader' 69 | }, 70 | { 71 | loader: 'css-loader', 72 | options: { 73 | sourceMap: !isProduction 74 | } 75 | }], 76 | })), 77 | }, 78 | { 79 | test: /\.scss$/, 80 | include: path.resolve(__dirname, '../src'), 81 | loader: ['css-hot-loader'].concat(ExtractTextPlugin.extract({ 82 | fallback: 'style-loader', 83 | use: [ 84 | { 85 | loader: 'css-loader', 86 | options: { 87 | sourceMap: !isProduction 88 | } 89 | }, 90 | { 91 | loader: 'resolve-url-loader', 92 | options: { 93 | sourceMap: !isProduction 94 | } 95 | }, 96 | { 97 | loader: 'sass-loader', 98 | options: { 99 | sourceMap: !isProduction 100 | } 101 | }, 102 | ], 103 | })) 104 | }, 105 | { 106 | test: /\.(png|jpe?g|gif|svg|xml|json)$/, 107 | include: path.resolve(__dirname, '../src'), 108 | use: { 109 | loader: 'file-loader', 110 | options: { 111 | name: 'assets/img/[name].[ext]', 112 | }, 113 | }, 114 | }, 115 | { 116 | test: /\.(ttf|eot)$/, 117 | include: path.resolve(__dirname, '../src'), 118 | use: { 119 | loader: 'file-loader', 120 | options: { 121 | name: 'assets/vendor/[name].[ext]', 122 | }, 123 | }, 124 | }, 125 | { 126 | test: /\.woff2?$/, 127 | include: path.resolve(__dirname, '../src'), 128 | use: { 129 | loader: 'url-loader', 130 | options: { 131 | limit: 10000, 132 | mimetype: 'application/font-woff', 133 | name: 'assets/vendor/[name].[ext]', 134 | }, 135 | }, 136 | }, 137 | { 138 | test: /\.(ttf|eot|woff2?|png|jpe?g|gif|svg)$/, 139 | include: /node_modules/, 140 | use: { 141 | loader: 'file-loader', 142 | query: { 143 | name: 'assets/vendor/[name].[ext]' 144 | }, 145 | }, 146 | }, 147 | ], 148 | }, 149 | }; 150 | --------------------------------------------------------------------------------