├── .eslintrc.json ├── .gitignore ├── .prettierrc ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── demo ├── .gitignore ├── client │ └── website │ │ ├── .gitattributes │ │ ├── .gitignore │ │ ├── .prettierrc │ │ ├── gatsby-config.js │ │ ├── package.json │ │ └── src │ │ ├── assets │ │ ├── README.md │ │ ├── contact.js │ │ ├── css │ │ │ ├── fontawesome-all.min.css │ │ │ ├── images │ │ │ │ ├── arrow.svg │ │ │ │ ├── ie │ │ │ │ │ └── banner-overlay.png │ │ │ │ └── overlay.png │ │ │ ├── main.css │ │ │ └── noscript.css │ │ ├── images │ │ │ ├── Elucid-Before-And-After-14.png │ │ │ ├── Elucid-Before-And-After-23.png │ │ │ ├── Elucid-Before-And-After-Too-Many.png │ │ │ ├── Elucid_Logo.jpg │ │ │ ├── Elucid_Logo1.jpg │ │ │ ├── banner.gif │ │ │ ├── icons │ │ │ │ ├── ELUCID-icons-github.svg │ │ │ │ └── ELUCID-icons-linkedin.svg │ │ │ ├── pic02.jpg │ │ │ ├── pic022.jpg │ │ │ ├── pic03.gif │ │ │ ├── pic04.gif │ │ │ ├── pic05.jpg │ │ │ ├── pic06.jpg │ │ │ ├── pic07.gif │ │ │ ├── pic07.jpg │ │ │ ├── pic076.jpg │ │ │ ├── team_photos │ │ │ │ ├── Gary.jpg │ │ │ │ ├── Khayal.jpg │ │ │ │ ├── Spencer.jpg │ │ │ │ └── Vivian.jpg │ │ │ └── website-icon.png │ │ ├── scss │ │ │ ├── libs │ │ │ │ ├── _breakpoints.scss │ │ │ │ ├── _functions.scss │ │ │ │ ├── _html-grid.scss │ │ │ │ ├── _mixins.scss │ │ │ │ ├── _vars.scss │ │ │ │ └── _vendor.scss │ │ │ ├── main.scss │ │ │ └── noscript.scss │ │ └── webfonts │ │ │ ├── fa-brands-400.eot │ │ │ ├── fa-brands-400.svg │ │ │ ├── fa-brands-400.ttf │ │ │ ├── fa-brands-400.woff │ │ │ ├── fa-brands-400.woff2 │ │ │ ├── fa-regular-400.eot │ │ │ ├── fa-regular-400.svg │ │ │ ├── fa-regular-400.ttf │ │ │ ├── fa-regular-400.woff │ │ │ ├── fa-regular-400.woff2 │ │ │ ├── fa-solid-900.eot │ │ │ ├── fa-solid-900.svg │ │ │ ├── fa-solid-900.ttf │ │ │ ├── fa-solid-900.woff │ │ │ └── fa-solid-900.woff2 │ │ ├── components │ │ ├── Banner.js │ │ ├── Footer.js │ │ ├── Graphiql.jsx │ │ ├── Header.js │ │ ├── Home1.js │ │ ├── Home2.js │ │ ├── Home3.js │ │ ├── Legal.js │ │ ├── MobileMenu.js │ │ ├── TeamMember.js │ │ └── layout.js │ │ └── pages │ │ ├── 404.js │ │ ├── demo.js │ │ ├── index.js │ │ └── team.js └── server │ ├── .gitignore │ ├── Procfile │ ├── database │ └── starwars_db_hardCoded.js.js │ ├── index.html │ ├── models │ └── model.js │ ├── package.json │ ├── schema │ ├── resolvers.js │ └── schema.js │ └── server.js └── elucid ├── .gitignore ├── .npmignore ├── README.md ├── __test__ └── handleResultWithError.test.js ├── assets ├── Elucid_Lamp_with_Text.png └── server-setup.jpg ├── package.json └── src ├── helpers ├── buildElucidMessage.js ├── elucidateHelper.js ├── getQueryFieldsWithArgs.js ├── getResponseFieldsWithNull.js ├── getSchemaFieldsWithNullableArgs.js └── handleResultWithError.js └── index.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "browser": true, 5 | "es2020": true, 6 | "jest": true 7 | }, 8 | "extends": [ 9 | "airbnb", 10 | "eslint:recommended", 11 | "prettier" 12 | ], 13 | "parserOptions": { 14 | "ecmaVersion": 11, 15 | "sourceType": "module" 16 | }, 17 | "plugins": ["prettier"], 18 | "rules": { 19 | "no-console": "warn", 20 | "prettier/prettier": "error" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | .DS_Store 3 | .vscode -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "singleQuote": true, 4 | "tabWidth": 2 5 | } 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Elucid 2 | 3 | The Elucid Team would like to thank you for your interest in helping to maintain and improve Elucid! 4 | 5 | ## If you plan on modifying the website, follow these steps beforehand 6 | 7 | There are 3 npm actions you need to run before working: 8 | - First go to website within demo and run `npm install` 9 | - Second run `npm run build` within the same directory 10 | - Third use `npm run develop` to launch the website 11 | 12 | Optional for server-side changes: 13 | - Go to server within demo and run `npm install` 14 | 15 | ## If you plan on modifying the package, follow these steps beforehand 16 | 17 | - Navigate to elucid folder and run `npm install` to download dependencies 18 | 19 | 20 | ## Reporting Bugs 21 | 22 | All code changes happen through Github Pull Requests and we actively welcome them. To submit your pull request, follow the steps below: 23 | 24 | ## Pull Requests 25 | 26 | 1. Fork the repo and create your branch from `main`. 27 | 2. If you've added code that should be tested, add tests. 28 | 3. If you've changed APIs, update the documentation. 29 | 4. Ensure the tests pass. 30 | 5. Make sure your code lints. 31 | 6. Submit a pull request. 32 | 33 | Note: Any contributions you make will be under the ISC Software License and your submissions are understood to be under the same that covers the project. Please reach out to the team if you have any questions. 34 | 35 | ## Issues 36 | 37 | We use GitHub issues to track bugs. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue. 38 | 4. Ensure the test suite passes. 39 | 5. Make sure your code lints. 40 | 6. Issue that pull request! 41 | 42 | Note: Any contributions you make will be under the MIT Software License and your submissions are understood to be under the same that covers the project. Please reach out to the team if you have any questions. 43 | 44 | 45 | ## Coding Style 46 | 47 | 2 spaces for indentation rather than tabs 48 | 80 character line length 49 | 50 | ## License 51 | 52 | By contributing, you agree that your contributions will be licensed under Elucid's ISC License. 53 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2021 Elucid 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | [![License: ISC](https://img.shields.io/badge/license-ISC-yellow.svg)](https://opensource.org/licenses/ISC) 4 | ![AppVeyor](https://img.shields.io/badge/build-passing-brightgreen.svg) 5 | ![AppVeyor](https://img.shields.io/badge/version-1.0.2-blue.svg) 6 | [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/oslabs-beta/Elucid/blob/main/CONTRIBUTING.md) 7 | 8 | # Elucid 9 | 10 | Elucid: Added Insight for GraphQL. 11 | 12 | Accelerated by [OS Labs](https://github.com/oslabs-beta/) and developed by [Khayal Alasgarov](https://github.com/Alasgaroff), [Vivian Wu](https://github.com/vw-in-github), [Spencer Szu](https://github.com/Szuay), and [Giovanni Iacobucci](https://github.com/garyiacobucci). 13 | 14 | ## Features 15 | 16 | - Extensible error detection and messaging for common graphQL issues deriving from server problems and bad queries. 17 | 18 | ## Installation 19 | 20 | Elucid can be installed as an npm package: 21 | 22 | - Download Elucid from npm in your terminal with `npm i elucid.js` 23 | 24 | ### Installing and connecting with Express: 25 | 26 | In its current form, Elucid extends express-graphql's server middleware, so you'll need your GraphQL HTTP server setup running Express (or a similar web framework that supports connect styled middleware, such as Connect or Restify) as a prerequisite. Please see the [express documentation](https://github.com/expressjs/express/blob/master/Readme.md) for more information on how to use the express framework. 27 | 28 | ### Usage notes 29 | 30 | - Currently, Elucid is in beta release and adds additional insight and status code manipulation for simple queries. 31 | - We intend to expand support in future releases to include multiple arguments, directives, variables, mutations and subscriptions. 32 | 33 | ## Getting started with Elucid 34 | 35 | Here's an example of a typical server.js setup running Elucid: 36 | 37 | ```js 38 | const express = require('express'); 39 | const path = require('path'); 40 | const schema = require('./schema/schema'); 41 | const resolvers = require('./schema/resolvers'); 42 | const elucid = require('elucid.js'); 43 | 44 | const PORT = 3000; 45 | 46 | const app = express(); 47 | 48 | app.use(express.json()); 49 | 50 | // Handle requests to GraphQL endpoint 51 | app.use('/graphql', elucid({ 52 | schema, 53 | resolvers, 54 | graphiql: true, 55 | pretty: true, 56 | })); 57 | ``` 58 | 59 | - Notice that after installation of its npm package, the Elucid function must be imported into your server file as a required module, 'elucid.js'. 60 | - Then, the elucid function is passed into the Express 'use' method as its second argument. 61 | - This is very similar to how express-graphql's 'graphqlHTTP' middleware function is used. In fact, the configuration object that elucid() takes as an argument passes its values through to an invocation of graphqlHTTP. That includes the keys 'schema' and 'resolvers' (note that these evaluate to your imported GraphQL schema and resolver files), as well as the config booleans 'graphiql' and 'pretty'. 62 | - At runtime, elucid() will kick off a sequence of events under the hood. GraphqlHTTP will process the request. If it doesn't catch an error on its own, Elucid's functionality will kick in to do its additional post-processing, which may include modifying the default response status code and returning a more detailed, useful context message in the 'elucid' property of the response object's 'extensions' property. 63 | 64 | ### Contribute to Elucid 65 | 66 | Interested in making a contribution to Elucid? [Click](https://github.com/oslabs-beta/Elucid/blob/main/CONTRIBUTING.md) for our open-source contribution guidelines. 67 | 68 | Happy bug hunting/data fetching! 69 | Team Elucid -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json -------------------------------------------------------------------------------- /demo/client/website/.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-vendored 2 | *.js linguist-vendored=false 3 | -------------------------------------------------------------------------------- /demo/client/website/.gitignore: -------------------------------------------------------------------------------- 1 | # Project dependencies 2 | .cache 3 | node_modules 4 | yarn-error.log 5 | package-lock.json 6 | yarn.lock 7 | 8 | # Build directory 9 | public 10 | -------------------------------------------------------------------------------- /demo/client/website/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "semi": false, 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /demo/client/website/gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | //pathPrefix: `/mygatsby`, 3 | siteMetadata: { 4 | title: 'Home', 5 | author: 'ELUCID TEAM', 6 | description: 'A Gatsby.js V2 Starter based on Landed by HTML5 UP', 7 | menuLinks: [ 8 | { 9 | name: 'Demo', 10 | link: '/demo', 11 | }, 12 | { 13 | name: 'Links', 14 | link: '#', 15 | items: [ 16 | { 17 | name: 'GitHub', 18 | link: 'https://github.com/oslabs-beta/Elucid', 19 | target:'_blank', 20 | 21 | }, 22 | { 23 | name: 'Medium', 24 | link: 'https://medium.com/@gioiacobucci/introducing-elucid-a-graphql-error-handler-for-the-rest-of-us-1edd161bc38a', 25 | target:'_blank', 26 | 27 | }, 28 | { 29 | name: 'NPM', 30 | link: 'https://www.npmjs.com/package/elucid.js', 31 | target:'_blank', 32 | 33 | }, 34 | ], 35 | }, 36 | { 37 | name: 'Team', 38 | link: '/team', 39 | }, 40 | { 41 | name: 'Install Elucid', 42 | link: 'https://www.npmjs.com/package/elucid.js', 43 | target:'_blank', 44 | cl: 'button primary', 45 | }, 46 | ], 47 | }, 48 | plugins: [ 49 | 'gatsby-plugin-react-helmet', 50 | { 51 | resolve: `gatsby-plugin-manifest`, 52 | options: { 53 | name: 'gatsby-starter-default', 54 | short_name: 'starter', 55 | start_url: '/', 56 | background_color: '#663399', 57 | theme_color: '#663399', 58 | display: 'minimal-ui', 59 | icon: 'src/assets/images/Elucid_Logo.jpg', // This path is relative to the root of the site. 60 | }, 61 | }, 62 | { 63 | resolve: 'gatsby-source-filesystem', 64 | options: { 65 | path: `${__dirname}/src/assets/images/`, 66 | name: 'images', 67 | }, 68 | }, 69 | 'gatsby-transformer-sharp', 70 | 'gatsby-plugin-sharp', 71 | 'gatsby-plugin-sass', 72 | { 73 | resolve: 'gatsby-plugin-html-attributes', 74 | options: { 75 | lang: 'en', 76 | }, 77 | }, 78 | 'gatsby-plugin-offline', 79 | ], 80 | } 81 | -------------------------------------------------------------------------------- /demo/client/website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-starter-landed-v2", 3 | "description": "Gatsby Starter - Landed V2", 4 | "version": "1.0.0", 5 | "author": "Vasilis Gakis", 6 | "dependencies": { 7 | "gatsby": "^2.18.1", 8 | "gatsby-image": "^2.2.33", 9 | "gatsby-plugin-html-attributes": "^1.0.5", 10 | "gatsby-plugin-manifest": "^2.2.29", 11 | "gatsby-plugin-offline": "^3.0.22", 12 | "gatsby-plugin-react-helmet": "^3.1.15", 13 | "gatsby-plugin-sass": "^2.1.23", 14 | "gatsby-plugin-sharp": "^2.3.2", 15 | "gatsby-source-filesystem": "^2.1.38", 16 | "gatsby-transformer-sharp": "^2.3.5", 17 | "graphiql": "^1.4.2", 18 | "node-sass": "^4.13.0", 19 | "react": "^16.12.0", 20 | "react-copy-to-clipboard": "^5.0.4", 21 | "react-dom": "^16.12.0", 22 | "react-helmet": "^5.2.1", 23 | "react-reveal": "^1.2.2", 24 | "react-scroll": "^1.7.14" 25 | }, 26 | "resolutions": { 27 | "react-helmet/react-side-effect": "2.1.0" 28 | }, 29 | "keywords": [ 30 | "gatsby" 31 | ], 32 | "license": "MIT", 33 | "scripts": { 34 | "build": "gatsby build", 35 | "develop": "gatsby develop", 36 | "format": "prettier --write '**/*.js'", 37 | "clean": "gatsby clean", 38 | "test": "echo \"Error: no test specified\" && exit 1" 39 | }, 40 | "devDependencies": { 41 | "prettier": "^1.19.1" 42 | }, 43 | "repository": { 44 | "type": "git", 45 | "url": "https://github.com/vasrush/gatsby-starter-landed" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /demo/client/website/src/assets/README.md: -------------------------------------------------------------------------------- 1 | # gatsby-starter-landed 2 | 3 | **This is a starter for Gatsby.js V2.** 4 | 5 | Gatsby.js V2 starter based on the Landed site template, designed by HTML5 UP. 6 | 7 | ## Preview 8 | 9 | https://gatsby-starter-landed.netlify.com 10 | 11 | ## Installation 12 | 13 | Install this starter : 14 |
15 | `gatsby new gatsby-starter-landed https://github.com/vasrush/gatsby-starter-landed` 16 | 17 | Run `gatsby develop` to start the dev site or `gatsby build` to build it for production. 18 | -------------------------------------------------------------------------------- /demo/client/website/src/assets/contact.js: -------------------------------------------------------------------------------- 1 | // import React from 'react' 2 | // import { Helmet } from 'react-helmet' 3 | // import Layout from '../components/layout' 4 | 5 | 6 | // const contact = () => ( 7 | // 8 | // 9 | // Contact - Elucid 10 | // 11 | // 12 | 13 | //
14 | //
15 | //
16 | //

Contact us

17 | //

18 | // Feel free to email us for any questions or concerns 19 | //

20 | //

21 | // elucid.elucidate@gmail.com 22 | //

23 | //
24 | //
25 | //
26 | //
27 | //
28 | // 29 | //
30 | //
31 | // 37 | //
38 | //
39 | // 45 | //
46 | //
47 | //
    48 | //
  • 49 | // 55 | //
  • 56 | //
  • 57 | // 58 | //
  • 59 | //
60 | //
61 | //
62 | //
63 | //
64 | //
65 | //
66 | //
67 | // ) 68 | 69 | // export default contact; 70 | -------------------------------------------------------------------------------- /demo/client/website/src/assets/css/fontawesome-all.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.9.0 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | .fa,.fab,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{animation:fa-spin 2s infinite linear}.fa-pulse{animation:fa-spin 1s infinite steps(8)}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adobe:before{content:"\f778"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-handshake:before{content:"\f2b5"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-wizard:before{content:"\f6e8"}.fa-haykal:before{content:"\f666"}.fa-hdd:before{content:"\f0a0"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:normal;font-display:auto;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands"}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:auto;src:url(../webfonts/fa-regular-400.eot);src:url(../webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.woff) format("woff"),url(../webfonts/fa-regular-400.ttf) format("truetype"),url(../webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-weight:400}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:auto;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.far,.fas{font-family:"Font Awesome 5 Free"}.fa,.fas{font-weight:900} -------------------------------------------------------------------------------- /demo/client/website/src/assets/css/images/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/client/website/src/assets/css/images/ie/banner-overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/css/images/ie/banner-overlay.png -------------------------------------------------------------------------------- /demo/client/website/src/assets/css/images/overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/css/images/overlay.png -------------------------------------------------------------------------------- /demo/client/website/src/assets/css/noscript.css: -------------------------------------------------------------------------------- 1 | /* 2 | Landed by HTML5 UP 3 | html5up.net | @ajlkn 4 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | */ 6 | 7 | /* Loader */ 8 | 9 | body.landing.is-preload:before { 10 | display: none; 11 | } 12 | 13 | body.landing.is-preload:after { 14 | display: none; 15 | } -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/Elucid-Before-And-After-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/Elucid-Before-And-After-14.png -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/Elucid-Before-And-After-23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/Elucid-Before-And-After-23.png -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/Elucid-Before-And-After-Too-Many.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/Elucid-Before-And-After-Too-Many.png -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/Elucid_Logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/Elucid_Logo.jpg -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/Elucid_Logo1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/Elucid_Logo1.jpg -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/banner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/banner.gif -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/icons/ELUCID-icons-github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/icons/ELUCID-icons-linkedin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/pic02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/pic02.jpg -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/pic022.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/pic022.jpg -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/pic03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/pic03.gif -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/pic04.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/pic04.gif -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/pic05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/pic05.jpg -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/pic06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/pic06.jpg -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/pic07.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/pic07.gif -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/pic07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/pic07.jpg -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/pic076.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/pic076.jpg -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/team_photos/Gary.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/team_photos/Gary.jpg -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/team_photos/Khayal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/team_photos/Khayal.jpg -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/team_photos/Spencer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/team_photos/Spencer.jpg -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/team_photos/Vivian.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/team_photos/Vivian.jpg -------------------------------------------------------------------------------- /demo/client/website/src/assets/images/website-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/images/website-icon.png -------------------------------------------------------------------------------- /demo/client/website/src/assets/scss/libs/_breakpoints.scss: -------------------------------------------------------------------------------- 1 | // breakpoints.scss v1.0 | @ajlkn | MIT licensed */ 2 | 3 | // Vars. 4 | 5 | /// Breakpoints. 6 | /// @var {list} 7 | $breakpoints: () !global; 8 | 9 | // Mixins. 10 | 11 | /// Sets breakpoints. 12 | /// @param {map} $x Breakpoints. 13 | @mixin breakpoints($x: ()) { 14 | $breakpoints: $x !global; 15 | } 16 | 17 | /// Wraps @content in a @media block targeting a specific orientation. 18 | /// @param {string} $orientation Orientation. 19 | @mixin orientation($orientation) { 20 | @media screen and (orientation: #{$orientation}) { 21 | @content; 22 | } 23 | } 24 | 25 | /// Wraps @content in a @media block using a given query. 26 | /// @param {string} $query Query. 27 | @mixin breakpoint($query: null) { 28 | 29 | $breakpoint: null; 30 | $op: null; 31 | $media: null; 32 | 33 | // Determine operator, breakpoint. 34 | 35 | // Greater than or equal. 36 | @if (str-slice($query, 0, 2) == '>=') { 37 | 38 | $op: 'gte'; 39 | $breakpoint: str-slice($query, 3); 40 | 41 | } 42 | 43 | // Less than or equal. 44 | @elseif (str-slice($query, 0, 2) == '<=') { 45 | 46 | $op: 'lte'; 47 | $breakpoint: str-slice($query, 3); 48 | 49 | } 50 | 51 | // Greater than. 52 | @elseif (str-slice($query, 0, 1) == '>') { 53 | 54 | $op: 'gt'; 55 | $breakpoint: str-slice($query, 2); 56 | 57 | } 58 | 59 | // Less than. 60 | @elseif (str-slice($query, 0, 1) == '<') { 61 | 62 | $op: 'lt'; 63 | $breakpoint: str-slice($query, 2); 64 | 65 | } 66 | 67 | // Not. 68 | @elseif (str-slice($query, 0, 1) == '!') { 69 | 70 | $op: 'not'; 71 | $breakpoint: str-slice($query, 2); 72 | 73 | } 74 | 75 | // Equal. 76 | @else { 77 | 78 | $op: 'eq'; 79 | $breakpoint: $query; 80 | 81 | } 82 | 83 | // Build media. 84 | @if ($breakpoint and map-has-key($breakpoints, $breakpoint)) { 85 | 86 | $a: map-get($breakpoints, $breakpoint); 87 | 88 | // Range. 89 | @if (type-of($a) == 'list') { 90 | 91 | $x: nth($a, 1); 92 | $y: nth($a, 2); 93 | 94 | // Max only. 95 | @if ($x == null) { 96 | 97 | // Greater than or equal (>= 0 / anything) 98 | @if ($op == 'gte') { 99 | $media: 'screen'; 100 | } 101 | 102 | // Less than or equal (<= y) 103 | @elseif ($op == 'lte') { 104 | $media: 'screen and (max-width: ' + $y + ')'; 105 | } 106 | 107 | // Greater than (> y) 108 | @elseif ($op == 'gt') { 109 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 110 | } 111 | 112 | // Less than (< 0 / invalid) 113 | @elseif ($op == 'lt') { 114 | $media: 'screen and (max-width: -1px)'; 115 | } 116 | 117 | // Not (> y) 118 | @elseif ($op == 'not') { 119 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 120 | } 121 | 122 | // Equal (<= y) 123 | @else { 124 | $media: 'screen and (max-width: ' + $y + ')'; 125 | } 126 | 127 | } 128 | 129 | // Min only. 130 | @else if ($y == null) { 131 | 132 | // Greater than or equal (>= x) 133 | @if ($op == 'gte') { 134 | $media: 'screen and (min-width: ' + $x + ')'; 135 | } 136 | 137 | // Less than or equal (<= inf / anything) 138 | @elseif ($op == 'lte') { 139 | $media: 'screen'; 140 | } 141 | 142 | // Greater than (> inf / invalid) 143 | @elseif ($op == 'gt') { 144 | $media: 'screen and (max-width: -1px)'; 145 | } 146 | 147 | // Less than (< x) 148 | @elseif ($op == 'lt') { 149 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 150 | } 151 | 152 | // Not (< x) 153 | @elseif ($op == 'not') { 154 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 155 | } 156 | 157 | // Equal (>= x) 158 | @else { 159 | $media: 'screen and (min-width: ' + $x + ')'; 160 | } 161 | 162 | } 163 | 164 | // Min and max. 165 | @else { 166 | 167 | // Greater than or equal (>= x) 168 | @if ($op == 'gte') { 169 | $media: 'screen and (min-width: ' + $x + ')'; 170 | } 171 | 172 | // Less than or equal (<= y) 173 | @elseif ($op == 'lte') { 174 | $media: 'screen and (max-width: ' + $y + ')'; 175 | } 176 | 177 | // Greater than (> y) 178 | @elseif ($op == 'gt') { 179 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 180 | } 181 | 182 | // Less than (< x) 183 | @elseif ($op == 'lt') { 184 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 185 | } 186 | 187 | // Not (< x and > y) 188 | @elseif ($op == 'not') { 189 | $media: 'screen and (max-width: ' + ($x - 1) + '), screen and (min-width: ' + ($y + 1) + ')'; 190 | } 191 | 192 | // Equal (>= x and <= y) 193 | @else { 194 | $media: 'screen and (min-width: ' + $x + ') and (max-width: ' + $y + ')'; 195 | } 196 | 197 | } 198 | 199 | } 200 | 201 | // String. 202 | @else { 203 | 204 | // Missing a media type? Prefix with "screen". 205 | @if (str-slice($a, 0, 1) == '(') { 206 | $media: 'screen and ' + $a; 207 | } 208 | 209 | // Otherwise, use as-is. 210 | @else { 211 | $media: $a; 212 | } 213 | 214 | } 215 | 216 | } 217 | 218 | // Output. 219 | @media #{$media} { 220 | @content; 221 | } 222 | 223 | } -------------------------------------------------------------------------------- /demo/client/website/src/assets/scss/libs/_functions.scss: -------------------------------------------------------------------------------- 1 | /// Removes a specific item from a list. 2 | /// @author Hugo Giraudel 3 | /// @param {list} $list List. 4 | /// @param {integer} $index Index. 5 | /// @return {list} Updated list. 6 | @function remove-nth($list, $index) { 7 | 8 | $result: null; 9 | 10 | @if type-of($index) != number { 11 | @warn "$index: #{quote($index)} is not a number for `remove-nth`."; 12 | } 13 | @else if $index == 0 { 14 | @warn "List index 0 must be a non-zero integer for `remove-nth`."; 15 | } 16 | @else if abs($index) > length($list) { 17 | @warn "List index is #{$index} but list is only #{length($list)} item long for `remove-nth`."; 18 | } 19 | @else { 20 | 21 | $result: (); 22 | $index: if($index < 0, length($list) + $index + 1, $index); 23 | 24 | @for $i from 1 through length($list) { 25 | 26 | @if $i != $index { 27 | $result: append($result, nth($list, $i)); 28 | } 29 | 30 | } 31 | 32 | } 33 | 34 | @return $result; 35 | 36 | } 37 | 38 | /// Gets a value from a map. 39 | /// @author Hugo Giraudel 40 | /// @param {map} $map Map. 41 | /// @param {string} $keys Key(s). 42 | /// @return {string} Value. 43 | @function val($map, $keys...) { 44 | 45 | @if nth($keys, 1) == null { 46 | $keys: remove-nth($keys, 1); 47 | } 48 | 49 | @each $key in $keys { 50 | $map: map-get($map, $key); 51 | } 52 | 53 | @return $map; 54 | 55 | } 56 | 57 | /// Gets a duration value. 58 | /// @param {string} $keys Key(s). 59 | /// @return {string} Value. 60 | @function _duration($keys...) { 61 | @return val($duration, $keys...); 62 | } 63 | 64 | /// Gets a font value. 65 | /// @param {string} $keys Key(s). 66 | /// @return {string} Value. 67 | @function _font($keys...) { 68 | @return val($font, $keys...); 69 | } 70 | 71 | /// Gets a misc value. 72 | /// @param {string} $keys Key(s). 73 | /// @return {string} Value. 74 | @function _misc($keys...) { 75 | @return val($misc, $keys...); 76 | } 77 | 78 | /// Gets a palette value. 79 | /// @param {string} $keys Key(s). 80 | /// @return {string} Value. 81 | @function _palette($keys...) { 82 | @return val($palette, $keys...); 83 | } 84 | 85 | /// Gets a size value. 86 | /// @param {string} $keys Key(s). 87 | /// @return {string} Value. 88 | @function _size($keys...) { 89 | @return val($size, $keys...); 90 | } -------------------------------------------------------------------------------- /demo/client/website/src/assets/scss/libs/_html-grid.scss: -------------------------------------------------------------------------------- 1 | // html-grid.scss v1.0 | @ajlkn | MIT licensed */ 2 | 3 | // Mixins. 4 | 5 | /// Initializes the current element as an HTML grid. 6 | /// @param {mixed} $gutters Gutters (either a single number to set both column/row gutters, or a list to set them individually). 7 | /// @param {mixed} $suffix Column class suffix (optional; either a single suffix or a list). 8 | @mixin html-grid($gutters: 1.5em, $suffix: '') { 9 | 10 | // Initialize. 11 | $cols: 12; 12 | $multipliers: 0, 0.25, 0.5, 1, 1.50, 2.00; 13 | $unit: 100% / $cols; 14 | 15 | // Suffixes. 16 | $suffixes: null; 17 | 18 | @if (type-of($suffix) == 'list') { 19 | $suffixes: $suffix; 20 | } 21 | @else { 22 | $suffixes: ($suffix); 23 | } 24 | 25 | // Gutters. 26 | $guttersCols: null; 27 | $guttersRows: null; 28 | 29 | @if (type-of($gutters) == 'list') { 30 | 31 | $guttersCols: nth($gutters, 1); 32 | $guttersRows: nth($gutters, 2); 33 | 34 | } 35 | @else { 36 | 37 | $guttersCols: $gutters; 38 | $guttersRows: 0; 39 | 40 | } 41 | 42 | // Row. 43 | display: flex; 44 | flex-wrap: wrap; 45 | box-sizing: border-box; 46 | align-items: stretch; 47 | 48 | // Columns. 49 | > * { 50 | box-sizing: border-box; 51 | } 52 | 53 | // Gutters. 54 | &.gtr-uniform { 55 | > * { 56 | > :last-child { 57 | margin-bottom: 0; 58 | } 59 | } 60 | } 61 | 62 | // Alignment. 63 | &.aln-left { 64 | justify-content: flex-start; 65 | } 66 | 67 | &.aln-center { 68 | justify-content: center; 69 | } 70 | 71 | &.aln-right { 72 | justify-content: flex-end; 73 | } 74 | 75 | &.aln-top { 76 | align-items: flex-start; 77 | } 78 | 79 | &.aln-middle { 80 | align-items: center; 81 | } 82 | 83 | &.aln-bottom { 84 | align-items: flex-end; 85 | } 86 | 87 | // Step through suffixes. 88 | @each $suffix in $suffixes { 89 | 90 | // Suffix. 91 | @if ($suffix != '') { 92 | $suffix: '-' + $suffix; 93 | } 94 | @else { 95 | $suffix: ''; 96 | } 97 | 98 | // Row. 99 | 100 | // Important. 101 | > .imp#{$suffix} { 102 | order: -1; 103 | } 104 | 105 | // Columns, offsets. 106 | @for $i from 1 through $cols { 107 | > .col-#{$i}#{$suffix} { 108 | width: $unit * $i; 109 | } 110 | 111 | > .off-#{$i}#{$suffix} { 112 | margin-left: $unit * $i; 113 | } 114 | } 115 | 116 | // Step through multipliers. 117 | @each $multiplier in $multipliers { 118 | 119 | // Gutters. 120 | $class: null; 121 | 122 | @if ($multiplier != 1) { 123 | $class: '.gtr-' + ($multiplier * 100); 124 | } 125 | 126 | &#{$class} { 127 | margin-top: ($guttersRows * $multiplier * -1); 128 | margin-left: ($guttersCols * $multiplier * -1); 129 | 130 | > * { 131 | padding: ($guttersRows * $multiplier) 0 0 ($guttersCols * $multiplier); 132 | } 133 | 134 | // Uniform. 135 | &.gtr-uniform { 136 | margin-top: $guttersCols * $multiplier * -1; 137 | 138 | > * { 139 | padding-top: $guttersCols * $multiplier; 140 | } 141 | } 142 | 143 | } 144 | 145 | } 146 | 147 | } 148 | 149 | } -------------------------------------------------------------------------------- /demo/client/website/src/assets/scss/libs/_mixins.scss: -------------------------------------------------------------------------------- 1 | /// Makes an element's :before pseudoelement a FontAwesome icon. 2 | /// @param {string} $content Optional content value to use. 3 | /// @param {string} $category Optional category to use. 4 | /// @param {string} $where Optional pseudoelement to target (before or after). 5 | @mixin icon($content: false, $category: regular, $where: before) { 6 | 7 | text-decoration: none; 8 | 9 | &:#{$where} { 10 | 11 | @if $content { 12 | content: $content; 13 | } 14 | 15 | -moz-osx-font-smoothing: grayscale; 16 | -webkit-font-smoothing: antialiased; 17 | display: inline-block; 18 | font-style: normal; 19 | font-variant: normal; 20 | text-rendering: auto; 21 | line-height: 1; 22 | text-transform: none !important; 23 | 24 | @if ($category == brands) { 25 | font-family: 'Font Awesome 5 Brands'; 26 | } 27 | @elseif ($category == solid) { 28 | font-family: 'Font Awesome 5 Free'; 29 | font-weight: 900; 30 | } 31 | @else { 32 | font-family: 'Font Awesome 5 Free'; 33 | font-weight: 400; 34 | } 35 | 36 | } 37 | 38 | } 39 | 40 | /// Applies padding to an element, taking the current element-margin value into account. 41 | /// @param {mixed} $tb Top/bottom padding. 42 | /// @param {mixed} $lr Left/right padding. 43 | /// @param {list} $pad Optional extra padding (in the following order top, right, bottom, left) 44 | /// @param {bool} $important If true, adds !important. 45 | @mixin padding($tb, $lr, $pad: (0,0,0,0), $important: null) { 46 | 47 | @if $important { 48 | $important: '!important'; 49 | } 50 | 51 | $x: 0.1em; 52 | 53 | @if unit(_size(element-margin)) == 'rem' { 54 | $x: 0.1rem; 55 | } 56 | 57 | padding: ($tb + nth($pad,1)) ($lr + nth($pad,2)) max($x, $tb - _size(element-margin) + nth($pad,3)) ($lr + nth($pad,4)) #{$important}; 58 | 59 | } 60 | 61 | /// Encodes a SVG data URL so IE doesn't choke (via codepen.io/jakob-e/pen/YXXBrp). 62 | /// @param {string} $svg SVG data URL. 63 | /// @return {string} Encoded SVG data URL. 64 | @function svg-url($svg) { 65 | 66 | $svg: str-replace($svg, '"', '\''); 67 | $svg: str-replace($svg, '%', '%25'); 68 | $svg: str-replace($svg, '<', '%3C'); 69 | $svg: str-replace($svg, '>', '%3E'); 70 | $svg: str-replace($svg, '&', '%26'); 71 | $svg: str-replace($svg, '#', '%23'); 72 | $svg: str-replace($svg, '{', '%7B'); 73 | $svg: str-replace($svg, '}', '%7D'); 74 | $svg: str-replace($svg, ';', '%3B'); 75 | 76 | @return url("data:image/svg+xml;charset=utf8,#{$svg}"); 77 | 78 | } -------------------------------------------------------------------------------- /demo/client/website/src/assets/scss/libs/_vars.scss: -------------------------------------------------------------------------------- 1 | // Misc. 2 | $misc: ( 3 | z-index-base: 10000, 4 | z-index-overlay: 100000, 5 | max-spotlight: 20 6 | ); 7 | 8 | // Duration. 9 | $duration: ( 10 | navPanel: 0.5s, 11 | transition: 0.2s, 12 | landing-fadein: 1.5s 13 | ); 14 | 15 | // Size. 16 | $size: ( 17 | border-radius: 4px, 18 | element-height: 3em, 19 | element-margin: 2em, 20 | navPanel: 275px, 21 | container-width: 70em, 22 | container-height: 70em 23 | ); 24 | 25 | // Font. 26 | $font: ( 27 | family: ('Montserrat', Helvetica, sans-serif), 28 | family-fixed: ('Courier New', monospace), 29 | weight: 200, 30 | weight-bold: 400 31 | ); 32 | 33 | // Palette. 34 | $palette: ( 35 | bg: #1c1d26, 36 | bg-transparent: rgba(23,24,32,0.95), 37 | fg-bold: #ffffff, 38 | fg: rgba(255,255,255,0.75), 39 | fg-light: rgba(255,255,255,0.5), 40 | fg-lighter: rgba(255,255,255,0.15), 41 | border: rgba(255,255,255,0.3), 42 | border-bg: rgba(255,255,255,0.075), 43 | border2: rgba(255,255,255,0.5), 44 | border2-bg: rgba(255,255,255,0.25), 45 | accent1: #f23869, 46 | accent2: #0b0340, 47 | accent2-transparent:rgba(39,40,51,0.965), 48 | accent3: #3621bf, 49 | accent4: #f249d6 50 | ); -------------------------------------------------------------------------------- /demo/client/website/src/assets/scss/libs/_vendor.scss: -------------------------------------------------------------------------------- 1 | // vendor.scss v1.0 | @ajlkn | MIT licensed */ 2 | 3 | // Vars. 4 | 5 | /// Vendor prefixes. 6 | /// @var {list} 7 | $vendor-prefixes: ( 8 | '-moz-', 9 | '-webkit-', 10 | '-ms-', 11 | '' 12 | ); 13 | 14 | /// Properties that should be vendorized. 15 | /// Data via caniuse.com, github.com/postcss/autoprefixer, and developer.mozilla.org 16 | /// @var {list} 17 | $vendor-properties: ( 18 | 19 | // Animation. 20 | 'animation', 21 | 'animation-delay', 22 | 'animation-direction', 23 | 'animation-duration', 24 | 'animation-fill-mode', 25 | 'animation-iteration-count', 26 | 'animation-name', 27 | 'animation-play-state', 28 | 'animation-timing-function', 29 | 30 | // Appearance. 31 | 'appearance', 32 | 33 | // Backdrop filter. 34 | 'backdrop-filter', 35 | 36 | // Background image options. 37 | 'background-clip', 38 | 'background-origin', 39 | 'background-size', 40 | 41 | // Box sizing. 42 | 'box-sizing', 43 | 44 | // Clip path. 45 | 'clip-path', 46 | 47 | // Filter effects. 48 | 'filter', 49 | 50 | // Flexbox. 51 | 'align-content', 52 | 'align-items', 53 | 'align-self', 54 | 'flex', 55 | 'flex-basis', 56 | 'flex-direction', 57 | 'flex-flow', 58 | 'flex-grow', 59 | 'flex-shrink', 60 | 'flex-wrap', 61 | 'justify-content', 62 | 'order', 63 | 64 | // Font feature. 65 | 'font-feature-settings', 66 | 'font-language-override', 67 | 'font-variant-ligatures', 68 | 69 | // Font kerning. 70 | 'font-kerning', 71 | 72 | // Fragmented borders and backgrounds. 73 | 'box-decoration-break', 74 | 75 | // Grid layout. 76 | 'grid-column', 77 | 'grid-column-align', 78 | 'grid-column-end', 79 | 'grid-column-start', 80 | 'grid-row', 81 | 'grid-row-align', 82 | 'grid-row-end', 83 | 'grid-row-start', 84 | 'grid-template-columns', 85 | 'grid-template-rows', 86 | 87 | // Hyphens. 88 | 'hyphens', 89 | 'word-break', 90 | 91 | // Masks. 92 | 'mask', 93 | 'mask-border', 94 | 'mask-border-outset', 95 | 'mask-border-repeat', 96 | 'mask-border-slice', 97 | 'mask-border-source', 98 | 'mask-border-width', 99 | 'mask-clip', 100 | 'mask-composite', 101 | 'mask-image', 102 | 'mask-origin', 103 | 'mask-position', 104 | 'mask-repeat', 105 | 'mask-size', 106 | 107 | // Multicolumn. 108 | 'break-after', 109 | 'break-before', 110 | 'break-inside', 111 | 'column-count', 112 | 'column-fill', 113 | 'column-gap', 114 | 'column-rule', 115 | 'column-rule-color', 116 | 'column-rule-style', 117 | 'column-rule-width', 118 | 'column-span', 119 | 'column-width', 120 | 'columns', 121 | 122 | // Object fit. 123 | 'object-fit', 124 | 'object-position', 125 | 126 | // Regions. 127 | 'flow-from', 128 | 'flow-into', 129 | 'region-fragment', 130 | 131 | // Scroll snap points. 132 | 'scroll-snap-coordinate', 133 | 'scroll-snap-destination', 134 | 'scroll-snap-points-x', 135 | 'scroll-snap-points-y', 136 | 'scroll-snap-type', 137 | 138 | // Shapes. 139 | 'shape-image-threshold', 140 | 'shape-margin', 141 | 'shape-outside', 142 | 143 | // Tab size. 144 | 'tab-size', 145 | 146 | // Text align last. 147 | 'text-align-last', 148 | 149 | // Text decoration. 150 | 'text-decoration-color', 151 | 'text-decoration-line', 152 | 'text-decoration-skip', 153 | 'text-decoration-style', 154 | 155 | // Text emphasis. 156 | 'text-emphasis', 157 | 'text-emphasis-color', 158 | 'text-emphasis-position', 159 | 'text-emphasis-style', 160 | 161 | // Text size adjust. 162 | 'text-size-adjust', 163 | 164 | // Text spacing. 165 | 'text-spacing', 166 | 167 | // Transform. 168 | 'transform', 169 | 'transform-origin', 170 | 171 | // Transform 3D. 172 | 'backface-visibility', 173 | 'perspective', 174 | 'perspective-origin', 175 | 'transform-style', 176 | 177 | // Transition. 178 | 'transition', 179 | 'transition-delay', 180 | 'transition-duration', 181 | 'transition-property', 182 | 'transition-timing-function', 183 | 184 | // Unicode bidi. 185 | 'unicode-bidi', 186 | 187 | // User select. 188 | 'user-select', 189 | 190 | // Writing mode. 191 | 'writing-mode', 192 | 193 | ); 194 | 195 | /// Values that should be vendorized. 196 | /// Data via caniuse.com, github.com/postcss/autoprefixer, and developer.mozilla.org 197 | /// @var {list} 198 | $vendor-values: ( 199 | 200 | // Cross fade. 201 | 'cross-fade', 202 | 203 | // Element function. 204 | 'element', 205 | 206 | // Filter function. 207 | 'filter', 208 | 209 | // Flexbox. 210 | 'flex', 211 | 'inline-flex', 212 | 213 | // Grab cursors. 214 | 'grab', 215 | 'grabbing', 216 | 217 | // Gradients. 218 | 'linear-gradient', 219 | 'repeating-linear-gradient', 220 | 'radial-gradient', 221 | 'repeating-radial-gradient', 222 | 223 | // Grid layout. 224 | 'grid', 225 | 'inline-grid', 226 | 227 | // Image set. 228 | 'image-set', 229 | 230 | // Intrinsic width. 231 | 'max-content', 232 | 'min-content', 233 | 'fit-content', 234 | 'fill', 235 | 'fill-available', 236 | 'stretch', 237 | 238 | // Sticky position. 239 | 'sticky', 240 | 241 | // Transform. 242 | 'transform', 243 | 244 | // Zoom cursors. 245 | 'zoom-in', 246 | 'zoom-out', 247 | 248 | ); 249 | 250 | // Functions. 251 | 252 | /// Removes a specific item from a list. 253 | /// @author Hugo Giraudel 254 | /// @param {list} $list List. 255 | /// @param {integer} $index Index. 256 | /// @return {list} Updated list. 257 | @function remove-nth($list, $index) { 258 | 259 | $result: null; 260 | 261 | @if type-of($index) != number { 262 | @warn "$index: #{quote($index)} is not a number for `remove-nth`."; 263 | } 264 | @else if $index == 0 { 265 | @warn "List index 0 must be a non-zero integer for `remove-nth`."; 266 | } 267 | @else if abs($index) > length($list) { 268 | @warn "List index is #{$index} but list is only #{length($list)} item long for `remove-nth`."; 269 | } 270 | @else { 271 | 272 | $result: (); 273 | $index: if($index < 0, length($list) + $index + 1, $index); 274 | 275 | @for $i from 1 through length($list) { 276 | 277 | @if $i != $index { 278 | $result: append($result, nth($list, $i)); 279 | } 280 | 281 | } 282 | 283 | } 284 | 285 | @return $result; 286 | 287 | } 288 | 289 | /// Replaces a substring within another string. 290 | /// @author Hugo Giraudel 291 | /// @param {string} $string String. 292 | /// @param {string} $search Substring. 293 | /// @param {string} $replace Replacement. 294 | /// @return {string} Updated string. 295 | @function str-replace($string, $search, $replace: '') { 296 | 297 | $index: str-index($string, $search); 298 | 299 | @if $index { 300 | @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); 301 | } 302 | 303 | @return $string; 304 | 305 | } 306 | 307 | /// Replaces a substring within each string in a list. 308 | /// @param {list} $strings List of strings. 309 | /// @param {string} $search Substring. 310 | /// @param {string} $replace Replacement. 311 | /// @return {list} Updated list of strings. 312 | @function str-replace-all($strings, $search, $replace: '') { 313 | 314 | @each $string in $strings { 315 | $strings: set-nth($strings, index($strings, $string), str-replace($string, $search, $replace)); 316 | } 317 | 318 | @return $strings; 319 | 320 | } 321 | 322 | // Mixins. 323 | 324 | /// Wraps @content in vendorized keyframe blocks. 325 | /// @param {string} $name Name. 326 | @mixin keyframes($name) { 327 | 328 | @-moz-keyframes #{$name} { @content; } 329 | @-webkit-keyframes #{$name} { @content; } 330 | @-ms-keyframes #{$name} { @content; } 331 | @keyframes #{$name} { @content; } 332 | 333 | } 334 | 335 | /// Vendorizes a declaration's property and/or value(s). 336 | /// @param {string} $property Property. 337 | /// @param {mixed} $value String/list of value(s). 338 | @mixin vendor($property, $value) { 339 | 340 | // Determine if property should expand. 341 | $expandProperty: index($vendor-properties, $property); 342 | 343 | // Determine if value should expand (and if so, add '-prefix-' placeholder). 344 | $expandValue: false; 345 | 346 | @each $x in $value { 347 | @each $y in $vendor-values { 348 | @if $y == str-slice($x, 1, str-length($y)) { 349 | 350 | $value: set-nth($value, index($value, $x), '-prefix-' + $x); 351 | $expandValue: true; 352 | 353 | } 354 | } 355 | } 356 | 357 | // Expand property? 358 | @if $expandProperty { 359 | @each $vendor in $vendor-prefixes { 360 | #{$vendor}#{$property}: #{str-replace-all($value, '-prefix-', $vendor)}; 361 | } 362 | } 363 | 364 | // Expand just the value? 365 | @elseif $expandValue { 366 | @each $vendor in $vendor-prefixes { 367 | #{$property}: #{str-replace-all($value, '-prefix-', $vendor)}; 368 | } 369 | } 370 | 371 | // Neither? Treat them as a normal declaration. 372 | @else { 373 | #{$property}: #{$value}; 374 | } 375 | 376 | } -------------------------------------------------------------------------------- /demo/client/website/src/assets/scss/main.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/vendor'; 5 | @import 'libs/breakpoints'; 6 | @import 'libs/html-grid'; 7 | @import url('../css/fontawesome-all.min.css'); 8 | /*@import url("https://fonts.googleapis.com/css?family=Roboto:100,300,100italic,300italic");*/ 9 | @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@200;400&display=swap'); 10 | 11 | /* 12 | Landed by HTML5 UP 13 | html5up.net | @ajlkn 14 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 15 | */ 16 | 17 | // Breakpoints. 18 | 19 | @include breakpoints(( 20 | xlarge: ( 1281px, 1680px ), 21 | large: ( 981px, 1280px ), 22 | medium: ( 737px, 980px ), 23 | small: ( 481px, 736px ), 24 | xsmall: ( null, 480px ), 25 | )); 26 | 27 | // Mixins. 28 | 29 | @mixin line-icon($bg: _palette(bg), $fg: _palette(fg-bold)) { 30 | @include icon; 31 | $size: 1px; 32 | 33 | &:before { 34 | color: $bg !important; 35 | text-shadow: $size 0 0 $fg, 36 | ($size * -1) 0 0 $fg, 37 | 0 $size 0 $fg, 38 | 0 ($size * -1) 0 $fg; 39 | } 40 | } 41 | 42 | $size-wrapper-pad-tb: 6em; 43 | $size-wrapper-pad-lr: 3em; 44 | 45 | // Reset. 46 | // Based on meyerweb.com/eric/tools/css/reset (v2.0 | 20110126 | License: public domain) 47 | 48 | html, body, div, span, applet, object, 49 | iframe, h1, h2, h3, h4, h5, h6, p, blockquote, 50 | pre, a, abbr, acronym, address, big, cite, 51 | code, del, dfn, em, img, ins, kbd, q, s, samp, 52 | small, strike, strong, sub, sup, tt, var, b, 53 | u, i, center, dl, dt, dd, ol, ul, li, fieldset, 54 | form, label, legend, table, caption, tbody, 55 | tfoot, thead, tr, th, td, article, aside, 56 | canvas, details, embed, figure, figcaption, 57 | footer, header, hgroup, menu, nav, output, ruby, 58 | section, summary, time, mark, audio, video { 59 | margin: 0; 60 | padding: 0; 61 | border: 0; 62 | font-size: 100%; 63 | font: inherit; 64 | vertical-align: baseline; 65 | } 66 | 67 | article, aside, details, figcaption, figure, 68 | footer, header, hgroup, menu, nav, section { 69 | display: block; 70 | } 71 | 72 | body { 73 | line-height: 1; 74 | } 75 | 76 | ol, ul { 77 | list-style:none; 78 | } 79 | 80 | blockquote, q { 81 | quotes: none; 82 | 83 | &:before, 84 | &:after { 85 | content: ''; 86 | content: none; 87 | } 88 | } 89 | 90 | table { 91 | border-collapse: collapse; 92 | border-spacing: 0; 93 | } 94 | 95 | body { 96 | -webkit-text-size-adjust: none; 97 | } 98 | 99 | mark { 100 | background-color: transparent; 101 | color: inherit; 102 | } 103 | 104 | input::-moz-focus-inner { 105 | border: 0; 106 | padding: 0; 107 | } 108 | 109 | input, select, textarea { 110 | -moz-appearance: none; 111 | -webkit-appearance: none; 112 | -ms-appearance: none; 113 | appearance: none; 114 | } 115 | 116 | /* Basic */ 117 | 118 | // Set box model to border-box. 119 | // Based on css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice 120 | html { 121 | box-sizing: border-box; 122 | } 123 | 124 | *, *:before, *:after { 125 | box-sizing: inherit; 126 | } 127 | 128 | html, body { 129 | background: _palette(bg); 130 | } 131 | 132 | body { 133 | 134 | // Stops initial animations until page loads. 135 | &.is-preload { 136 | *, *:before, *:after { 137 | @include vendor('animation', 'none !important'); 138 | @include vendor('transition', 'none !important'); 139 | } 140 | } 141 | 142 | } 143 | 144 | body, input, select, textarea { 145 | color: _palette(fg); 146 | font-family: _font(family); 147 | font-size: 15pt; 148 | font-weight: _font(weight); 149 | line-height: 1.75em; 150 | } 151 | 152 | a { 153 | @include vendor('transition', ('border-color #{_duration(transition)} ease-in-out', 'color #{_duration(transition)} ease-in-out')); 154 | border-bottom: dotted 1px; 155 | color: _palette(accent1); 156 | text-decoration: none; 157 | 158 | &:hover { 159 | color: _palette(accent1) !important; 160 | border-bottom-color: transparent; 161 | } 162 | } 163 | 164 | strong, b { 165 | color: _palette(fg-bold); 166 | font-weight: _font(weight-bold); 167 | } 168 | 169 | em, i { 170 | font-style: italic; 171 | } 172 | 173 | p { 174 | margin: 0 0 _size(element-margin) 0; 175 | } 176 | 177 | h1, h2, h3, h4, h5, h6 { 178 | color: _palette(fg-bold); 179 | font-weight: _font(weight-bold); 180 | line-height: 1em; 181 | margin: 0 0 (_size(element-margin) * 0.5) 0; 182 | 183 | a { 184 | color: inherit; 185 | border: 0; 186 | } 187 | } 188 | 189 | h2 { 190 | font-size: 2em; 191 | line-height: 1.5em; 192 | letter-spacing: -0.025em; 193 | } 194 | 195 | h3 { 196 | font-size: 1.35em; 197 | line-height: 1.5em; 198 | } 199 | 200 | h4 { 201 | font-size: 1.1em; 202 | line-height: 1.5em; 203 | } 204 | 205 | h5 { 206 | font-size: 0.9em; 207 | line-height: 1.5em; 208 | } 209 | 210 | h6 { 211 | font-size: 0.7em; 212 | line-height: 1.5em; 213 | } 214 | 215 | sub { 216 | font-size: 0.8em; 217 | position: relative; 218 | top: 0.5em; 219 | } 220 | 221 | sup { 222 | font-size: 0.8em; 223 | position: relative; 224 | top: -0.5em; 225 | } 226 | 227 | hr { 228 | border: 0; 229 | border-bottom: solid 1px _palette(border); 230 | margin: (_size(element-margin) * 1.5) 0; 231 | 232 | &.major { 233 | margin: (_size(element-margin) * 2) 0; 234 | } 235 | } 236 | 237 | blockquote { 238 | border-left: solid 4px _palette(border); 239 | font-style: italic; 240 | margin: 0 0 _size(element-margin) 0; 241 | padding: 0.5em 0 0.5em 2em; 242 | } 243 | 244 | code { 245 | background: _palette(border-bg); 246 | border-radius: _size(border-radius); 247 | font-family: _font(family-fixed); 248 | font-size: 0.9em; 249 | margin: 0 0.25em; 250 | padding: 0.25em 0.65em; 251 | } 252 | 253 | pre { 254 | -webkit-overflow-scrolling: touch; 255 | font-family: _font(family-fixed); 256 | font-size: 0.9em; 257 | margin: 0 0 _size(element-margin) 0; 258 | 259 | code { 260 | display: block; 261 | line-height: 1.75em; 262 | padding: 1em 1.5em; 263 | overflow-x: auto; 264 | } 265 | } 266 | 267 | .align-left { 268 | text-align: left; 269 | } 270 | 271 | .align-center { 272 | text-align: center; 273 | } 274 | 275 | .align-right { 276 | text-align: right; 277 | } 278 | 279 | /* Loader */ 280 | 281 | // Spinner 282 | 283 | @include keyframes('spinner-show') { 284 | 0% { opacity: 0; } 285 | 100% { opacity: 1; } 286 | } 287 | 288 | @include keyframes('spinner-hide') { 289 | 0% { color: _palette(fg-lighter); z-index: _misc(z-index-overlay) + 1; @include vendor('transform', 'scale(1) rotate(0deg)'); } 290 | 99% { color: _palette(bg); z-index: _misc(z-index-overlay) + 1; @include vendor('transform', 'scale(0.5) rotate(360deg)'); } 291 | 100% { color: _palette(bg); z-index: -1; @include vendor('transform', 'scale(0.5) rotate(360deg)'); } 292 | } 293 | 294 | @include keyframes('spinner-rotate') { 295 | 0% { @include vendor('transform', 'scale(1) rotate(0deg)'); } 296 | 100% { @include vendor('transform', 'scale(1) rotate(360deg)'); } 297 | } 298 | 299 | // Overlay 300 | 301 | @include keyframes('overlay-hide') { 302 | 0% { opacity: 1; z-index: _misc(z-index-overlay); } 303 | 15% { opacity: 1; z-index: _misc(z-index-overlay); } 304 | 99% { opacity: 0; z-index: _misc(z-index-overlay); } 305 | 100% { opacity: 0; z-index: -1; } 306 | } 307 | 308 | body .landing { 309 | @include icon(false, solid); 310 | 311 | // Spinner (inactive) 312 | 313 | &:before { 314 | @include vendor('animation', ('spinner-show 1.5s 1 0.25s ease forwards', 'spinner-hide 0.25s ease-in-out forwards !important')); 315 | @include vendor('transform-origin', '50% 50%'); 316 | 317 | color: _palette(fg-lighter); 318 | content: '\f1ce'; 319 | cursor: default; 320 | display: block; 321 | font-size: 2em; 322 | height: 2em; 323 | left: 50%; 324 | line-height: 2em; 325 | margin: -1em 0 0 -1em; 326 | opacity: 0; 327 | position: fixed; 328 | text-align: center; 329 | top: 50%; 330 | width: 2em; 331 | z-index: -1; 332 | } 333 | 334 | // Overlay (inactive) 335 | 336 | &:after { 337 | @include vendor('animation', 'overlay-hide #{_duration(landing-fadein)} ease-in forwards !important'); 338 | background: _palette(bg); 339 | content: ''; 340 | display: block; 341 | height: 100%; 342 | left: 0; 343 | opacity: 0; 344 | position: fixed; 345 | top: 0; 346 | width: 100%; 347 | z-index: -1; 348 | } 349 | 350 | &.is-preload { 351 | 352 | // Spinner (active) 353 | 354 | &:before { 355 | @include vendor('animation', ('spinner-show 1.5s 1 0.25s ease forwards', 'spinner-rotate 0.75s infinite linear !important')); 356 | z-index: _misc(z-index-overlay) + 1; 357 | } 358 | 359 | // Overlay (active) 360 | 361 | &:after { 362 | @include vendor('animation', 'none !important'); 363 | opacity: 1; 364 | z-index: _misc(z-index-overlay); 365 | } 366 | 367 | } 368 | } 369 | 370 | @media (-webkit-min-device-pixel-ratio: 2) { 371 | body .landing:before { 372 | line-height: 2.025em; 373 | } 374 | } 375 | 376 | /* Container */ 377 | 378 | .container { 379 | margin: 0 auto; 380 | max-width: calc(100% - #{_size(element-margin) * 2}); 381 | width: _size(container-width); 382 | 383 | &.xsmall { 384 | width: (_size(container-width) * 0.25); 385 | } 386 | 387 | &.small { 388 | width: (_size(container-width) * 0.5); 389 | } 390 | 391 | &.medium { 392 | width: (_size(container-width) * 0.75); 393 | } 394 | 395 | &.large { 396 | width: (_size(container-width) * 1.25); 397 | } 398 | 399 | &.xlarge { 400 | width: (_size(container-width) * 1.5); 401 | } 402 | 403 | &.max { 404 | width: 100%; 405 | } 406 | 407 | @include breakpoint('<=large') { 408 | width: 90%; 409 | max-width: 100%; 410 | } 411 | 412 | @include breakpoint('<=medium') { 413 | width: 100% !important; 414 | } 415 | } 416 | 417 | /* Row */ 418 | 419 | .row { 420 | @include html-grid(2.5em); 421 | 422 | @include breakpoint('<=xlarge') { 423 | @include html-grid(2.5em, 'xlarge'); 424 | } 425 | 426 | @include breakpoint('<=large') { 427 | @include html-grid(2.5em, 'large'); 428 | } 429 | 430 | @include breakpoint('<=medium') { 431 | @include html-grid(2.5em, 'medium'); 432 | } 433 | 434 | @include breakpoint('<=small') { 435 | @include html-grid(2.5em, 'small'); 436 | } 437 | 438 | @include breakpoint('<=xsmall') { 439 | @include html-grid(2.5em, 'xsmall'); 440 | } 441 | } 442 | 443 | /* Section/Article */ 444 | 445 | 446 | section, article { 447 | &.special { 448 | text-align: center; 449 | } 450 | } 451 | 452 | header { 453 | p { 454 | color: _palette(fg-bold); 455 | position: relative; 456 | margin: 0 0 (_size(element-margin) * 0.75) 0; 457 | } 458 | 459 | h2 + p { 460 | font-size: 1.25em; 461 | margin-top: (_size(element-margin) * -0.5); 462 | line-height: 1.75em; 463 | } 464 | 465 | h3 + p { 466 | font-size: 1.1em; 467 | margin-top: (_size(element-margin) * -0.4); 468 | line-height: 1.75em; 469 | } 470 | 471 | h4 + p, 472 | h5 + p, 473 | h6 + p { 474 | font-size: 0.9em; 475 | margin-top: (_size(element-margin) * -0.3); 476 | line-height: 1.5em; 477 | } 478 | 479 | &.major { 480 | margin: 0 0 (_size(element-margin) * 2) 0; 481 | position: relative; 482 | text-align: center; 483 | 484 | &:after { 485 | background: _palette(accent1); 486 | content: ''; 487 | display: inline-block; 488 | height: 0.2em; 489 | max-width: 20em; 490 | width: 75%; 491 | } 492 | } 493 | } 494 | 495 | footer { 496 | &.major { 497 | margin: (_size(element-margin) * 2) 0 0 0; 498 | } 499 | } 500 | 501 | /* Form */ 502 | 503 | form { 504 | margin: 0 0 _size(element-margin) 0; 505 | 506 | &.cta { 507 | max-width: 35em; 508 | margin-left: auto; 509 | margin-right: auto; 510 | } 511 | } 512 | 513 | label { 514 | color: _palette(fg-bold); 515 | display: block; 516 | font-size: 0.9em; 517 | font-weight: _font(weight-bold); 518 | margin: 0 0 (_size(element-margin) * 0.5) 0; 519 | } 520 | 521 | input[type="text"], 522 | input[type="password"], 523 | input[type="email"], 524 | select, 525 | textarea { 526 | @include vendor('appearance', 'none'); 527 | @include vendor('transition', 'border-color #{_duration(transition)} ease-in-out'); 528 | background: transparent; 529 | border-radius: _size(border-radius); 530 | border: solid 1px _palette(border); 531 | color: inherit; 532 | display: block; 533 | outline: 0; 534 | padding: 0 1em; 535 | text-decoration: none; 536 | width: 100%; 537 | 538 | &:invalid { 539 | box-shadow: none; 540 | } 541 | 542 | &:focus { 543 | border-color: _palette(accent1); 544 | } 545 | } 546 | 547 | select { 548 | background-image: svg-url(""); 549 | background-size: 1.25rem; 550 | background-repeat: no-repeat; 551 | background-position: calc(100% - 1rem) center; 552 | height: _size(element-height); 553 | padding-right: _size(element-height); 554 | text-overflow: ellipsis; 555 | 556 | option { 557 | color: _palette(fg-bold); 558 | background: _palette(bg); 559 | } 560 | 561 | &:focus { 562 | &::-ms-value { 563 | background-color: transparent; 564 | } 565 | } 566 | 567 | &::-ms-expand { 568 | display: none; 569 | } 570 | } 571 | 572 | input[type="text"], 573 | input[type="password"], 574 | input[type="email"], 575 | select { 576 | height: _size(element-height); 577 | } 578 | 579 | textarea { 580 | padding: 0.75em 1em; 581 | } 582 | 583 | // input[type="checkbox"], 584 | // input[type="radio"], { 585 | // @include vendor('appearance', 'none'); 586 | // display: block; 587 | // float: left; 588 | // margin-right: -2em; 589 | // opacity: 0; 590 | // width: 1em; 591 | // z-index: -1; 592 | 593 | // & + label { 594 | // @include icon(false, solid); 595 | // color: _palette(fg); 596 | // cursor: pointer; 597 | // display: inline-block; 598 | // font-size: 1em; 599 | // font-weight: _font(weight); 600 | // padding-left: (_size(element-height) * 0.6) + 0.75em; 601 | // padding-right: 0.75em; 602 | // position: relative; 603 | 604 | // &:before { 605 | // border-radius: _size(border-radius); 606 | // border: solid 1px _palette(border); 607 | // content: ''; 608 | // display: inline-block; 609 | // font-size: 0.8em; 610 | // height: (_size(element-height) * 0.75); 611 | // left: 0; 612 | // line-height: (_size(element-height) * 0.75); 613 | // position: absolute; 614 | // text-align: center; 615 | // top: 0; 616 | // width: (_size(element-height) * 0.75); 617 | // } 618 | // } 619 | 620 | // &:checked + label { 621 | // &:before { 622 | // background: _palette(border2-bg); 623 | // color: _palette(fg-bold); 624 | // content: '\f00c'; 625 | // } 626 | // } 627 | 628 | // &:focus + label { 629 | // &:before { 630 | // border-color: _palette(accent1); 631 | // } 632 | // } 633 | // } 634 | 635 | input[type="checkbox"] { 636 | & + label { 637 | &:before { 638 | border-radius: _size(border-radius); 639 | } 640 | } 641 | } 642 | 643 | input[type="radio"] { 644 | & + label { 645 | &:before { 646 | border-radius: 100%; 647 | } 648 | } 649 | } 650 | 651 | ::-webkit-input-placeholder { 652 | color: _palette(fg-light) !important; 653 | opacity: 1.0; 654 | } 655 | 656 | :-moz-placeholder { 657 | color: _palette(fg-light) !important; 658 | opacity: 1.0; 659 | } 660 | 661 | ::-moz-placeholder { 662 | color: _palette(fg-light) !important; 663 | opacity: 1.0; 664 | } 665 | 666 | :-ms-input-placeholder { 667 | color: _palette(fg-light) !important; 668 | opacity: 1.0; 669 | } 670 | 671 | /* Box */ 672 | 673 | .box { 674 | border-radius: _size(border-radius); 675 | border: solid 1px _palette(border); 676 | margin-bottom: _size(element-margin); 677 | padding: 1.5em; 678 | 679 | > :last-child, 680 | > :last-child > :last-child, 681 | > :last-child > :last-child > :last-child { 682 | margin-bottom: 0; 683 | } 684 | 685 | &.alt { 686 | border: 0; 687 | border-radius: 0; 688 | padding: 0; 689 | } 690 | } 691 | 692 | /* Icon */ 693 | 694 | .icon { 695 | @include icon; 696 | border-bottom: none; 697 | position: relative; 698 | 699 | > .label { 700 | display: none; 701 | } 702 | 703 | &:before { 704 | line-height: inherit; 705 | } 706 | 707 | &.solid { 708 | &:before { 709 | font-weight: 900 !important; 710 | } 711 | } 712 | 713 | &.brands { 714 | &:before { 715 | font-family: 'Font Awesome 5 Brands' !important; 716 | } 717 | } 718 | 719 | &.alt { 720 | @include line-icon; 721 | } 722 | 723 | &.major { 724 | background: _palette(accent2); 725 | border-radius: 100%; 726 | cursor: default; 727 | display: inline-block; 728 | height: 6em; 729 | line-height: 5.65em; 730 | margin: 0 0 _size(element-margin) 0; 731 | text-align: center; 732 | width: 6em; 733 | 734 | &:before { 735 | font-size: 2.25em; 736 | } 737 | 738 | &.alt { 739 | @include line-icon(_palette(accent2)); 740 | } 741 | } 742 | } 743 | 744 | /* Image */ 745 | 746 | .image { 747 | border-radius: _size(border-radius); 748 | border: 0; 749 | display: inline-block; 750 | position: relative; 751 | overflow: hidden; 752 | 753 | &:before { 754 | content: ''; 755 | display: block; 756 | position: absolute; 757 | left: 0; 758 | top: 0; 759 | background-image: url('../css/images/overlay.png'); 760 | width: 100%; 761 | height: 100%; 762 | z-index: -1; 763 | } 764 | 765 | img { 766 | border-radius: _size(border-radius); 767 | display: block; 768 | } 769 | 770 | &.left { 771 | float: left; 772 | margin: 0 1.5em 1em 0; 773 | top: 0.25em; 774 | } 775 | 776 | &.right { 777 | float: right; 778 | margin: 0 0 1em 1.5em; 779 | top: 0.25em; 780 | } 781 | 782 | &.left, 783 | &.right { 784 | max-width: 40%; 785 | 786 | img { 787 | width: 100%; 788 | } 789 | } 790 | 791 | &.fit { 792 | display: block; 793 | margin: 0 0 _size(element-margin) 0; 794 | width: 100%; 795 | 796 | img { 797 | width: 100%; 798 | } 799 | } 800 | } 801 | 802 | /* Graphiql */ 803 | .graphiql { 804 | margin: auto; 805 | max-width: 100%; 806 | margin-bottom: 2px; 807 | height: 800px; 808 | } 809 | /* Team */ 810 | #team-elucid { 811 | max-width: 1600px; 812 | margin: 0 auto; 813 | display:flex; 814 | flex-wrap: wrap; 815 | } 816 | 817 | // #team { 818 | // display: flex; 819 | // flex-direction: column; 820 | // align-items: center; 821 | // font-size: 18px; 822 | // } 823 | 824 | .profiles { 825 | flex-basis: 12%; 826 | 827 | // border: 1px solid rgb(29, 158, 104); 828 | padding: 0.3rem; 829 | flex-grow: 1; 830 | max-width: 25%; 831 | min-width: 12rem; 832 | margin: 0 1rem 2rem 2rem; 833 | // box-shadow: 1px 1px 10px rgba(22, 22, 22, 0.915); 834 | } 835 | 836 | .profiles img { 837 | display: center; 838 | height: 13em; 839 | margin: 1em 0 2em 0; 840 | vertical-align: middle; 841 | width: 10em; 842 | } 843 | .team-member-name { 844 | font-weight: bold; 845 | margin-bottom: 10px; 846 | letter-spacing: 5.2px; 847 | } 848 | 849 | .social-icons img { 850 | width: 5rem; 851 | margin-top: 10px; 852 | margin-left: 10px; 853 | } 854 | /* List */ 855 | 856 | ol { 857 | list-style: decimal; 858 | margin: 0 0 _size(element-margin) 0; 859 | padding-left: 1.25em; 860 | 861 | li { 862 | padding-left: 0.25em; 863 | } 864 | } 865 | 866 | ul { 867 | list-style: disc; 868 | margin: 0 0 _size(element-margin) 0; 869 | padding-left: 1em; 870 | 871 | li { 872 | padding-left: 0.5em; 873 | } 874 | 875 | &.alt { 876 | list-style: none; 877 | padding-left: 0; 878 | 879 | li { 880 | border-top: solid 1px _palette(border); 881 | padding: 0.5em 0; 882 | 883 | &:first-child { 884 | border-top: 0; 885 | padding-top: 0; 886 | } 887 | } 888 | } 889 | 890 | } 891 | 892 | dl { 893 | margin: 0 0 _size(element-margin) 0; 894 | } 895 | 896 | /* Icons */ 897 | 898 | ul.icons { 899 | cursor: default; 900 | list-style: none; 901 | padding-left: 0; 902 | 903 | li { 904 | display: inline-block; 905 | height: 2.5em; 906 | line-height: 2.5em; 907 | padding: 0 0.5em; 908 | 909 | .icon { 910 | font-size: 0.8em; 911 | 912 | &:before { 913 | font-size: 2em; 914 | } 915 | } 916 | } 917 | } 918 | 919 | /* Actions */ 920 | 921 | ul.actions { 922 | @include vendor('display', 'flex'); 923 | cursor: default; 924 | list-style: none; 925 | margin-left: (_size(element-margin) * -0.5); 926 | padding-left: 0; 927 | 928 | li { 929 | padding: 0 0 0 (_size(element-margin) * 0.5); 930 | vertical-align: middle; 931 | } 932 | 933 | &.special { 934 | @include vendor('justify-content', 'center'); 935 | width: 100%; 936 | margin-left: 0; 937 | 938 | li { 939 | &:first-child { 940 | padding-left: 0; 941 | } 942 | } 943 | } 944 | 945 | &.stacked { 946 | @include vendor('flex-direction', 'column'); 947 | margin-left: 0; 948 | 949 | li { 950 | padding: (_size(element-margin) * 0.65) 0 0 0; 951 | 952 | &:first-child { 953 | padding-top: 0; 954 | } 955 | } 956 | } 957 | 958 | &.fit { 959 | width: calc(100% + #{_size(element-margin) * 0.5}); 960 | 961 | li { 962 | @include vendor('flex-grow', '1'); 963 | @include vendor('flex-shrink', '1'); 964 | width: 100%; 965 | 966 | > * { 967 | width: 100%; 968 | } 969 | } 970 | 971 | &.stacked { 972 | width: 100%; 973 | } 974 | } 975 | 976 | @include breakpoint('<=xsmall') { 977 | &:not(.fixed) { 978 | @include vendor('flex-direction', 'column'); 979 | margin-left: 0; 980 | width: 100% !important; 981 | 982 | li { 983 | @include vendor('flex-grow', '1'); 984 | @include vendor('flex-shrink', '1'); 985 | padding: (_size(element-margin) * 0.5) 0 0 0; 986 | text-align: center; 987 | width: 100%; 988 | 989 | > * { 990 | width: 100%; 991 | } 992 | 993 | &:first-child { 994 | padding-top: 0; 995 | } 996 | 997 | input[type="submit"], 998 | input[type="reset"], 999 | input[type="button"], 1000 | button, 1001 | .button { 1002 | width: 100%; 1003 | 1004 | &.icon { 1005 | &:before { 1006 | margin-left: -0.5em; 1007 | } 1008 | } 1009 | } 1010 | } 1011 | } 1012 | } 1013 | } 1014 | 1015 | /* Table */ 1016 | 1017 | .table-wrapper { 1018 | -webkit-overflow-scrolling: touch; 1019 | overflow-x: auto; 1020 | } 1021 | 1022 | table { 1023 | margin: 0 0 _size(element-margin) 0; 1024 | width: 100%; 1025 | 1026 | tbody { 1027 | tr { 1028 | border: solid 1px _palette(border); 1029 | border-left: 0; 1030 | border-right: 0; 1031 | 1032 | &:nth-child(2n + 1) { 1033 | background-color: _palette(border-bg); 1034 | } 1035 | } 1036 | } 1037 | 1038 | td { 1039 | padding: 0.75em 0.75em; 1040 | } 1041 | 1042 | th { 1043 | color: _palette(fg-bold); 1044 | font-size: 0.9em; 1045 | font-weight: _font(weight-bold); 1046 | padding: 0 0.75em 0.75em 0.75em; 1047 | text-align: left; 1048 | } 1049 | 1050 | thead { 1051 | border-bottom: solid 1px _palette(border); 1052 | } 1053 | 1054 | tfoot { 1055 | border-top: solid 1px _palette(border); 1056 | } 1057 | 1058 | &.alt { 1059 | border-collapse: separate; 1060 | 1061 | tbody { 1062 | tr { 1063 | td { 1064 | border: solid 1px _palette(border); 1065 | border-left-width: 0; 1066 | border-top-width: 0; 1067 | 1068 | &:first-child { 1069 | border-left-width: 1px; 1070 | } 1071 | } 1072 | 1073 | &:first-child { 1074 | td { 1075 | border-top-width: 1px; 1076 | } 1077 | } 1078 | } 1079 | } 1080 | 1081 | thead { 1082 | border-bottom: 0; 1083 | } 1084 | 1085 | tfoot { 1086 | border-top: 0; 1087 | } 1088 | } 1089 | } 1090 | 1091 | /* Button */ 1092 | 1093 | input[type="submit"], 1094 | input[type="reset"], 1095 | input[type="button"], 1096 | .button { 1097 | @include vendor('appearance', 'none'); 1098 | @include vendor('transition', ('background-color #{_duration(transition)} ease-in-out', 'color #{_duration(transition)} ease-in-out', 'box-shadow #{_duration(transition)} ease-in-out')); 1099 | background-color: transparent; 1100 | border-radius: _size(border-radius); 1101 | border: 0; 1102 | box-shadow: inset 0 0 0 1px _palette(border); 1103 | color: _palette(fg-bold) !important; 1104 | cursor: pointer; 1105 | display: inline-block; 1106 | font-weight: _font(weight-bold); 1107 | height: _size(element-height); 1108 | line-height: _size(element-height); 1109 | padding: 0 2.25em; 1110 | text-align: center; 1111 | text-decoration: none; 1112 | white-space: nowrap; 1113 | 1114 | &:hover, &:active { 1115 | box-shadow: inset 0 0 0 1px _palette(accent1); 1116 | color: _palette(accent1) !important; 1117 | } 1118 | 1119 | &:active { 1120 | background-color: transparentize(_palette(accent1), 0.85); 1121 | } 1122 | 1123 | &.icon { 1124 | &:before { 1125 | margin-right: 0.5em; 1126 | } 1127 | } 1128 | 1129 | &.fit { 1130 | width: 100%; 1131 | } 1132 | 1133 | &.small { 1134 | font-size: 0.8em; 1135 | } 1136 | 1137 | &.large { 1138 | font-size: 1.35em; 1139 | } 1140 | 1141 | &.primary { 1142 | background-color: _palette(accent1); 1143 | box-shadow: none; 1144 | color: _palette(fg-bold) !important; 1145 | 1146 | &:hover { 1147 | background-color: lighten(_palette(accent1), 5); 1148 | } 1149 | 1150 | &:active { 1151 | background-color: darken(_palette(accent1), 5); 1152 | } 1153 | } 1154 | 1155 | &.disabled, 1156 | &:disabled { 1157 | background-color: _palette(border) !important; 1158 | box-shadow: none !important; 1159 | color: _palette(fg-bold) !important; 1160 | cursor: default; 1161 | opacity: 0.25; 1162 | } 1163 | } 1164 | 1165 | /* Goto Next */ 1166 | 1167 | .goto-next { 1168 | border: 0; 1169 | bottom: 0; 1170 | display: block; 1171 | height: 5em; 1172 | left: 50%; 1173 | margin: 0 0 0 -5em; 1174 | overflow: hidden; 1175 | position: absolute; 1176 | text-indent: 10em; 1177 | white-space: nowrap; 1178 | width: 10em; 1179 | z-index: 1; 1180 | cursor: pointer; 1181 | 1182 | &:before { 1183 | background-image: url('../css/images/arrow.svg'); 1184 | background-position: center center; 1185 | background-repeat: no-repeat; 1186 | background-size: contain; 1187 | content: ''; 1188 | display: block; 1189 | height: 1.5em; 1190 | left: 50%; 1191 | margin: -0.75em 0 0 -1em; 1192 | position: absolute; 1193 | top: 50%; 1194 | width: 2em; 1195 | z-index: 1; 1196 | } 1197 | } 1198 | 1199 | /* Spotlight */ 1200 | 1201 | .spotlight { 1202 | background-attachment: fixed; 1203 | background-position: center center; 1204 | background-size: cover; 1205 | box-shadow: 0 0.25em 0.5em 0 rgba(0,0,0,0.25); 1206 | height: 100vh; 1207 | overflow: hidden; 1208 | position: relative; 1209 | 1210 | // Force spotlights to stack in reverse order (needed for our box 1211 | // shadows to overlap stuff in the right direction). 1212 | @for $i from 1 through _misc(max-spotlight) { 1213 | &:nth-last-of-type(#{$i}) { 1214 | z-index: #{$i}; 1215 | } 1216 | } 1217 | 1218 | &:before { 1219 | background-image: url('../css/images/overlay.png'); 1220 | content: ''; 1221 | display: block; 1222 | height: 100%; 1223 | left: 0; 1224 | top: 0; 1225 | width: 100%; 1226 | } 1227 | 1228 | .image.main { 1229 | display: none; 1230 | 1231 | img { 1232 | position: relative; 1233 | } 1234 | } 1235 | 1236 | .content { 1237 | @include vendor('transform', 'translate(0,0)'); 1238 | @include vendor('transition', 'transform 5s ease, opacity 3s ease'); 1239 | background: _palette(bg-transparent); 1240 | border-style: solid; 1241 | opacity: 1; 1242 | position: absolute; 1243 | } 1244 | 1245 | .goto-next { 1246 | @include vendor('transform', 'translate(0,0)'); 1247 | @include vendor('transition', 'transform 0.75s ease, opacity 1s ease-in'); 1248 | @include vendor('transition-delay', '0.5s'); 1249 | opacity: 1; 1250 | } 1251 | 1252 | &.top, &.bottom { 1253 | .content { 1254 | left: 0; 1255 | padding: ($size-wrapper-pad-tb * 0.85) 0 (($size-wrapper-pad-tb * 0.85) - _size(element-margin)) 0; 1256 | width: 100%; 1257 | } 1258 | } 1259 | 1260 | &.top { 1261 | .content { 1262 | border-bottom-width: 0.35em; 1263 | top: 0; 1264 | } 1265 | } 1266 | 1267 | &.bottom { 1268 | .content { 1269 | border-top-width: 0.35em; 1270 | bottom: 0; 1271 | } 1272 | } 1273 | 1274 | &.left, &.right { 1275 | .content { 1276 | height: 101%; 1277 | padding: $size-wrapper-pad-tb $size-wrapper-pad-lr; 1278 | top: 0; 1279 | width: 28em; 1280 | } 1281 | } 1282 | 1283 | &.left { 1284 | .content { 1285 | border-right-width: 0.35em; 1286 | left: 0; 1287 | } 1288 | } 1289 | 1290 | &.right { 1291 | .content { 1292 | border-left-width: 0.35em; 1293 | right: 0; 1294 | } 1295 | } 1296 | 1297 | &.style1 { 1298 | .content { 1299 | border-color: _palette(accent1); 1300 | } 1301 | } 1302 | 1303 | &.style2 { 1304 | .content { 1305 | border-color: _palette(accent3); 1306 | } 1307 | } 1308 | 1309 | &.style3 { 1310 | .content { 1311 | border-color: _palette(accent4); 1312 | } 1313 | } 1314 | 1315 | &.inactive { 1316 | .content { 1317 | opacity: 0; 1318 | } 1319 | 1320 | .goto-next { 1321 | @include vendor('transform', 'translate(0,1.5em)'); 1322 | /*opacity: 0;*/ 1323 | } 1324 | 1325 | &.top { 1326 | .content { 1327 | @include vendor('transform', 'translate(0,-5em)'); 1328 | } 1329 | } 1330 | 1331 | &.bottom { 1332 | .content { 1333 | @include vendor('transform', 'translate(0,5em)'); 1334 | } 1335 | } 1336 | 1337 | &.left { 1338 | .content { 1339 | @include vendor('transform', 'translate(-5em,0)'); 1340 | } 1341 | } 1342 | 1343 | &.right { 1344 | .content { 1345 | @include vendor('transform', 'translate(5em,0)'); 1346 | } 1347 | } 1348 | } 1349 | } 1350 | 1351 | body.is-touch { 1352 | .spotlight { 1353 | background-attachment: scroll; 1354 | } 1355 | } 1356 | 1357 | /* Wrapper */ 1358 | 1359 | .wrapper { 1360 | padding: $size-wrapper-pad-tb 0 ($size-wrapper-pad-tb - _size(element-margin)) 0; 1361 | 1362 | // &.style1 { 1363 | // } 1364 | 1365 | &.style2 { 1366 | background: _palette(accent1); 1367 | 1368 | input[type="text"], 1369 | input[type="password"], 1370 | input[type="email"], 1371 | select, 1372 | textarea { 1373 | &:focus { 1374 | border-color: _palette(border2); 1375 | } 1376 | } 1377 | 1378 | input[type="submit"], 1379 | input[type="reset"], 1380 | input[type="button"], 1381 | .button { 1382 | &:hover, &:active { 1383 | background-color: _palette(border-bg) !important; 1384 | box-shadow: inset 0 0 0 1px _palette(border2) !important; 1385 | color: _palette(fg-bold) !important; 1386 | } 1387 | 1388 | &:active { 1389 | background-color: _palette(border2-bg) !important; 1390 | } 1391 | 1392 | &.primary { 1393 | background-color: _palette(fg-bold); 1394 | color: _palette(accent1) !important; 1395 | 1396 | &:hover, &:active { 1397 | background-color: _palette(border-bg) !important; 1398 | box-shadow: inset 0 0 0 1px _palette(border2) !important; 1399 | color: _palette(fg-bold) !important; 1400 | } 1401 | 1402 | &:active { 1403 | background-color: _palette(border2-bg) !important; 1404 | } 1405 | } 1406 | } 1407 | } 1408 | 1409 | &.fade-down { 1410 | > .container { 1411 | @include vendor('transform', 'translate(0,0)'); 1412 | @include vendor('transition', 'transform 1s ease, opacity 1s ease'); 1413 | opacity: 1; 1414 | } 1415 | 1416 | &.inactive { 1417 | > .container { 1418 | @include vendor('transform', 'translate(0,-1em)'); 1419 | opacity: 0; 1420 | } 1421 | } 1422 | } 1423 | 1424 | &.fade-up { 1425 | > .container { 1426 | @include vendor('transform', 'translate(0,0)'); 1427 | @include vendor('transition', 'transform 1s ease, opacity 1s ease'); 1428 | opacity: 1; 1429 | } 1430 | 1431 | &.inactive { 1432 | > .container { 1433 | @include vendor('transform', 'translate(0,1em)'); 1434 | opacity: 0; 1435 | } 1436 | } 1437 | } 1438 | 1439 | &.fade { 1440 | > .container { 1441 | @include vendor('transition', 'opacity 1s ease'); 1442 | opacity: 1; 1443 | } 1444 | 1445 | &.inactive { 1446 | > .container { 1447 | opacity: 0; 1448 | } 1449 | } 1450 | } 1451 | } 1452 | 1453 | /* Dropotron */ 1454 | 1455 | .dropotron { 1456 | background: _palette(accent2-transparent); 1457 | border-radius: _size(border-radius); 1458 | box-shadow: 0 0.075em 0.35em 0 rgba(0,0,0,0.125); 1459 | list-style: none; 1460 | margin-top: calc(-0.25em + 1px); 1461 | min-width: 12em; 1462 | padding: 0.25em 0; 1463 | 1464 | > li { 1465 | border-top: solid 1px rgba(255,255,255,0.035); 1466 | padding: 0; 1467 | 1468 | a, span { 1469 | border: 0; 1470 | color: _palette(fg); 1471 | display: block; 1472 | padding: 0.1em 1em; 1473 | text-decoration: none; 1474 | } 1475 | 1476 | &:first-child { 1477 | border-top: 0; 1478 | } 1479 | 1480 | &.active { 1481 | > a, > span { 1482 | color: _palette(accent1); 1483 | } 1484 | } 1485 | } 1486 | 1487 | &.level-0 { 1488 | font-size: 0.8em; 1489 | margin-top: 1em; 1490 | 1491 | &:before { 1492 | @include vendor('transform', 'rotate(45deg)'); 1493 | background: _palette(accent2); 1494 | content: ''; 1495 | display: block; 1496 | height: 1em; 1497 | position: absolute; 1498 | right: 1.5em; 1499 | top: -0.5em; 1500 | width: 1em; 1501 | } 1502 | } 1503 | } 1504 | 1505 | body .landing { 1506 | .dropotron { 1507 | &.level-0 { 1508 | margin-top: 0; 1509 | } 1510 | } 1511 | } 1512 | 1513 | /* Header */ 1514 | 1515 | #page-wrapper { 1516 | padding-top: 3.5em; 1517 | } 1518 | 1519 | #header { 1520 | background: _palette(accent2-transparent); 1521 | box-shadow: 0 0 0.25em 0 rgba(0,0,0,0.25); 1522 | cursor: default; 1523 | height: 3.5em; 1524 | left: 0; 1525 | line-height: 3.5em; 1526 | position: fixed; 1527 | top: 0; 1528 | width: 100%; 1529 | z-index: 100; 1530 | 1531 | h1 { 1532 | height: inherit; 1533 | left: 1.25em; 1534 | line-height: inherit; 1535 | margin: 0; 1536 | position: absolute; 1537 | top: 0; 1538 | } 1539 | 1540 | nav { 1541 | position: absolute; 1542 | right: 1em; 1543 | top: 0; 1544 | 1545 | ul { 1546 | margin: 0; 1547 | 1548 | li { 1549 | display: inline-block; 1550 | margin-left: 1em; 1551 | 1552 | a, span { 1553 | border: 0; 1554 | color: inherit; 1555 | display: inline-block; 1556 | height: inherit; 1557 | line-height: inherit; 1558 | outline: 0; 1559 | 1560 | &.button { 1561 | height: 2em; 1562 | line-height: 2em; 1563 | padding: 0 1.25em; 1564 | } 1565 | 1566 | &:not(.button):before { 1567 | margin-right: 0.5em; 1568 | } 1569 | } 1570 | 1571 | &.active { 1572 | > a, > span { 1573 | color: _palette(accent1); 1574 | } 1575 | } 1576 | 1577 | // > ul { 1578 | // /*display: none;*/ 1579 | // } 1580 | } 1581 | } 1582 | } 1583 | } 1584 | 1585 | body .landing { 1586 | #page-wrapper { 1587 | padding-top: 0; 1588 | } 1589 | 1590 | #header { 1591 | background: transparent; 1592 | box-shadow: none; 1593 | position: absolute; 1594 | } 1595 | } 1596 | 1597 | /* Banner */ 1598 | 1599 | #banner { 1600 | background-attachment: fixed; 1601 | background-color: _palette(accent2); 1602 | background-image: url('../images/banner.gif'); 1603 | background-position: center center; 1604 | background-size: cover; 1605 | box-shadow: 0 1.25em 0.5em 0 rgba(111, 13, 168, 0.25); 1606 | min-height: 100vh; 1607 | position: relative; 1608 | text-align: center; 1609 | z-index: (_misc(max-spotlight) + 1); 1610 | 1611 | &:before { 1612 | content: ''; 1613 | display: inline-block; 1614 | height: 100vh; 1615 | vertical-align: middle; 1616 | width: 1%; 1617 | } 1618 | 1619 | &:after { 1620 | @include vendor('background-image', ('linear-gradient(top, #{_palette(bg-transparent)}, #{_palette(bg-transparent)})', 'url("../css/images/overlay.png");')); 1621 | content: ''; 1622 | display: block; 1623 | height: 100%; 1624 | left: 0; 1625 | position: absolute; 1626 | top: 0; 1627 | width: 100%; 1628 | } 1629 | 1630 | .content { 1631 | display: inline-block; 1632 | margin-right: 1%; 1633 | max-width: 95%; 1634 | padding: $size-wrapper-pad-tb; 1635 | position: relative; 1636 | text-align: right; 1637 | vertical-align: middle; 1638 | z-index: 1; 1639 | 1640 | header { 1641 | display: inline-block; 1642 | vertical-align: middle; 1643 | 1644 | h2 { 1645 | font-size: 3.5em; 1646 | margin: 0; 1647 | } 1648 | 1649 | p { 1650 | margin: (_size(element-margin) * .25) 0 0 0; 1651 | top: 0; 1652 | } 1653 | } 1654 | 1655 | .image { 1656 | border-radius: 100%; 1657 | display: inline-block; 1658 | height: 18em; 1659 | margin-left: 3em; 1660 | vertical-align: middle; 1661 | width: 18em; 1662 | 1663 | img { 1664 | border-radius: 100%; 1665 | display: block; 1666 | width: 100%; 1667 | } 1668 | } 1669 | } 1670 | } 1671 | 1672 | body.is-touch { 1673 | #banner { 1674 | background-attachment: scroll; 1675 | } 1676 | } 1677 | 1678 | /* Footer */ 1679 | 1680 | #footer { 1681 | background: _palette(accent2); 1682 | padding: $size-wrapper-pad-tb 0; 1683 | text-align: center; 1684 | 1685 | .icons { 1686 | .icon { 1687 | &.alt { 1688 | @include line-icon(_palette(accent2), _palette(fg-light)); 1689 | } 1690 | } 1691 | } 1692 | 1693 | .copyright { 1694 | color: _palette(fg-light); 1695 | font-size: 0.8em; 1696 | line-height: 1em; 1697 | margin: 2em 0 0 0; 1698 | padding: 0; 1699 | text-align: center; 1700 | 1701 | li { 1702 | border-left: solid 1px _palette(border); 1703 | display: inline-block; 1704 | list-style: none; 1705 | margin-left: 1.5em; 1706 | padding-left: 1.5em; 1707 | 1708 | &:first-child { 1709 | border-left: 0; 1710 | margin-left: 0; 1711 | padding-left: 0; 1712 | } 1713 | 1714 | a { 1715 | color: inherit; 1716 | } 1717 | } 1718 | } 1719 | } 1720 | 1721 | /* XLarge */ 1722 | 1723 | @include breakpoint('<=xlarge') { 1724 | 1725 | /* Basic */ 1726 | 1727 | body, input, select, textarea { 1728 | font-size: 13pt; 1729 | } 1730 | 1731 | } 1732 | 1733 | /* Large */ 1734 | 1735 | @include breakpoint('<=large') { 1736 | 1737 | $size-wrapper-pad-tb: 4.5em; 1738 | $size-wrapper-pad-lr: 2.5em; 1739 | 1740 | /* Basic */ 1741 | 1742 | body, input, select, textarea { 1743 | font-size: 11.5pt; 1744 | } 1745 | 1746 | /* Spotlight */ 1747 | 1748 | .spotlight { 1749 | &.top { 1750 | .content { 1751 | padding: ($size-wrapper-pad-tb * 0.85) 0 (($size-wrapper-pad-tb * 0.85) - _size(element-margin)) 0; 1752 | } 1753 | } 1754 | 1755 | &.bottom { 1756 | .content { 1757 | padding: ($size-wrapper-pad-tb * 0.85) 0 (($size-wrapper-pad-tb * 1.1) - _size(element-margin)) 0; 1758 | } 1759 | } 1760 | 1761 | &.left, &.right { 1762 | .content { 1763 | padding: $size-wrapper-pad-tb $size-wrapper-pad-lr; 1764 | width: 25em; 1765 | } 1766 | } 1767 | } 1768 | 1769 | /* Wrapper */ 1770 | 1771 | .wrapper { 1772 | padding: $size-wrapper-pad-tb 0 ($size-wrapper-pad-tb - _size(element-margin)) 0; 1773 | } 1774 | 1775 | /* Dropotron */ 1776 | 1777 | .dropotron { 1778 | &.level-0 { 1779 | font-size: 1em; 1780 | } 1781 | } 1782 | 1783 | /* Banner */ 1784 | 1785 | #banner { 1786 | .content { 1787 | padding: $size-wrapper-pad-tb; 1788 | } 1789 | } 1790 | 1791 | /* Footer */ 1792 | 1793 | #footer { 1794 | padding: $size-wrapper-pad-tb 0; 1795 | } 1796 | 1797 | } 1798 | 1799 | /* Medium */ 1800 | 1801 | @include breakpoint('<=medium') { 1802 | 1803 | $size-wrapper-pad-tb: 4.5em; 1804 | $size-wrapper-pad-lr: 2.5em; 1805 | 1806 | /* Basic */ 1807 | 1808 | body, input, select, textarea { 1809 | font-size: 12pt; 1810 | } 1811 | 1812 | /* Spotlight */ 1813 | 1814 | .spotlight { 1815 | background-attachment: scroll; 1816 | height: auto; 1817 | 1818 | .image.main { 1819 | display: block; 1820 | margin: 0; 1821 | max-height: 40vh; 1822 | overflow: hidden; 1823 | } 1824 | 1825 | .content { 1826 | background-color: _palette(bg); 1827 | border-width: 0 !important; 1828 | border-top-width: 0.35em !important; 1829 | bottom: auto !important; 1830 | left: auto !important; 1831 | padding: $size-wrapper-pad-tb $size-wrapper-pad-lr ($size-wrapper-pad-tb - _size(element-margin)) $size-wrapper-pad-lr !important; 1832 | position: relative; 1833 | right: auto !important; 1834 | text-align: center; 1835 | top: auto !important; 1836 | width: 100% !important; 1837 | 1838 | ul.actions { 1839 | @include vendor('justify-content', 'center'); 1840 | width: 100%; 1841 | margin-left: 0; 1842 | 1843 | li { 1844 | &:first-child { 1845 | padding-left: 0; 1846 | } 1847 | } 1848 | } 1849 | } 1850 | 1851 | .goto-next { 1852 | display: none; 1853 | } 1854 | } 1855 | 1856 | /* Wrapper */ 1857 | 1858 | .wrapper { 1859 | padding: $size-wrapper-pad-tb $size-wrapper-pad-lr ($size-wrapper-pad-tb - _size(element-margin)) $size-wrapper-pad-lr; 1860 | } 1861 | 1862 | /* Banner */ 1863 | 1864 | #banner { 1865 | background-attachment: scroll; 1866 | 1867 | .goto-next { 1868 | height: 7em; 1869 | } 1870 | 1871 | .content { 1872 | padding: ($size-wrapper-pad-tb * 2) 0; 1873 | text-align: center; 1874 | 1875 | header { 1876 | display: block; 1877 | margin: 0 0 _size(element-margin) 0; 1878 | text-align: center; 1879 | } 1880 | 1881 | .image { 1882 | margin: 0; 1883 | } 1884 | } 1885 | } 1886 | 1887 | /* Footer */ 1888 | 1889 | #footer { 1890 | padding: $size-wrapper-pad-tb 0; 1891 | } 1892 | 1893 | } 1894 | 1895 | /* Small */ 1896 | 1897 | #navPanel, #titleBar { 1898 | display: none; 1899 | } 1900 | 1901 | @include breakpoint('<=small') { 1902 | 1903 | $size-wrapper-pad-tb: 3.25em; 1904 | $size-wrapper-pad-lr: 1.5em; 1905 | 1906 | /* Basic */ 1907 | 1908 | html, body { 1909 | overflow-x: hidden; 1910 | } 1911 | 1912 | body, input, select, textarea { 1913 | font-size: 12pt; 1914 | } 1915 | 1916 | h2 { 1917 | font-size: 1.5em; 1918 | } 1919 | 1920 | h3 { 1921 | font-size: 1.2em; 1922 | } 1923 | 1924 | h4 { 1925 | font-size: 1em; 1926 | } 1927 | 1928 | /* Section/Article */ 1929 | 1930 | header { 1931 | p { 1932 | br { 1933 | display: none; 1934 | } 1935 | } 1936 | 1937 | h2 + p { 1938 | font-size: 1em; 1939 | } 1940 | 1941 | h3 + p { 1942 | font-size: 1em; 1943 | } 1944 | 1945 | h4 + p, 1946 | h5 + p, 1947 | h6 + p { 1948 | font-size: 0.9em; 1949 | } 1950 | 1951 | &.major { 1952 | margin: 0 0 _size(element-margin) 0; 1953 | } 1954 | } 1955 | 1956 | /* Goto Next */ 1957 | 1958 | .goto-next { 1959 | &:before { 1960 | height: 0.8em; 1961 | margin: -0.4em 0 0 -0.6em; 1962 | width: 1.2em; 1963 | } 1964 | } 1965 | 1966 | /* Spotlight */ 1967 | 1968 | .spotlight { 1969 | box-shadow: 0 0.125em 0.5em 0 rgba(0,0,0,0.25); 1970 | 1971 | .image.main { 1972 | max-height: 60vh; 1973 | } 1974 | 1975 | .content { 1976 | border-top-width: 0.2em !important; 1977 | padding: $size-wrapper-pad-tb $size-wrapper-pad-lr ($size-wrapper-pad-tb - _size(element-margin)) $size-wrapper-pad-lr !important; 1978 | } 1979 | } 1980 | 1981 | /* Wrapper */ 1982 | 1983 | .wrapper { 1984 | padding: $size-wrapper-pad-tb $size-wrapper-pad-lr ($size-wrapper-pad-tb - _size(element-margin)) $size-wrapper-pad-lr; 1985 | } 1986 | 1987 | /* Header */ 1988 | 1989 | #header { 1990 | display: none; 1991 | } 1992 | 1993 | /* Banner */ 1994 | 1995 | #banner { 1996 | box-shadow: 0 0.125em 0.5em 0 rgba(0,0,0,0.25); 1997 | min-height: calc(100vh - 44px); 1998 | 1999 | &:before { 2000 | height: calc(100vh - 44px); 2001 | } 2002 | 2003 | .content { 2004 | padding: ($size-wrapper-pad-tb * 1.25) $size-wrapper-pad-lr ($size-wrapper-pad-tb * 1.5) $size-wrapper-pad-lr; 2005 | 2006 | header { 2007 | h2 { 2008 | font-size: 1.5em; 2009 | } 2010 | } 2011 | 2012 | .image { 2013 | height: 9em; 2014 | width: 9em; 2015 | } 2016 | } 2017 | } 2018 | 2019 | /* Nav */ 2020 | 2021 | #page-wrapper { 2022 | @include vendor('backface-visibility', 'hidden'); 2023 | @include vendor('transition', 'transform #{_duration(navPanel)} ease'); 2024 | padding-bottom: 1px; 2025 | padding-top: 44px !important; 2026 | } 2027 | 2028 | #titleBar { 2029 | @include vendor('backface-visibility', 'hidden'); 2030 | @include vendor('transition', 'transform #{_duration(navPanel)} ease'); 2031 | display: block; 2032 | height: 44px; 2033 | left: 0; 2034 | position: fixed; 2035 | top: 0; 2036 | width: 100%; 2037 | z-index: _misc(z-index-base) + 1; 2038 | background: _palette(accent2); 2039 | box-shadow: 0 0.125em 0.125em 0 rgba(0,0,0,0.125); 2040 | 2041 | .title { 2042 | color: _palette(fg-bold); 2043 | display: block; 2044 | font-weight: _font(weight-bold); 2045 | height: 44px; 2046 | line-height: 44px; 2047 | text-align: center; 2048 | 2049 | a { 2050 | color: inherit; 2051 | border: 0; 2052 | } 2053 | } 2054 | 2055 | .toggle { 2056 | @include icon(false, solid); 2057 | height: 60px; 2058 | left: 0; 2059 | position: absolute; 2060 | top: 0; 2061 | width: 90px; 2062 | outline: 0; 2063 | border: 0; 2064 | 2065 | &:before { 2066 | background: _palette(accent1); 2067 | color: _palette(fg-light); 2068 | content: '\f0c9'; 2069 | display: block; 2070 | font-size: 18px; 2071 | height: 44px; 2072 | left: 0; 2073 | line-height: 44px; 2074 | position: absolute; 2075 | text-align: center; 2076 | top: 0; 2077 | width: 54px; 2078 | } 2079 | } 2080 | } 2081 | 2082 | #navPanel { 2083 | @include vendor('backface-visibility', 'hidden'); 2084 | @include vendor('transform', 'translateX(#{_size(navPanel) * -1})'); 2085 | @include vendor('transition', ('transform #{_duration(navPanel)} ease')); 2086 | display: block; 2087 | height: 100%; 2088 | left: 0; 2089 | overflow-y: auto; 2090 | position: fixed; 2091 | top: 0; 2092 | width: _size(navPanel); 2093 | z-index: _misc(z-index-base) + 2; 2094 | background: darken(_palette(bg), 2); 2095 | padding: 0.75em 1.25em; 2096 | 2097 | .link { 2098 | border: 0; 2099 | border-top: solid 1px transparentize(_palette(border), 0.25); 2100 | color: _palette(fg); 2101 | display: block; 2102 | height: 3em; 2103 | line-height: 3em; 2104 | text-decoration: none; 2105 | 2106 | &:hover { 2107 | color: inherit !important; 2108 | } 2109 | 2110 | &:first-child { 2111 | border-top: 0; 2112 | } 2113 | 2114 | &.depth-0 { 2115 | color: _palette(fg-bold); 2116 | font-weight: _font(weight-bold); 2117 | } 2118 | 2119 | .indent-1 { display: inline-block; width: 1.25em; } 2120 | .indent-2 { display: inline-block; width: 2.5em; } 2121 | .indent-3 { display: inline-block; width: 3.75em; } 2122 | .indent-4 { display: inline-block; width: 5em; } 2123 | .indent-5 { display: inline-block; width: 6.25em; } 2124 | } 2125 | } 2126 | 2127 | body .navPanel-visible { 2128 | /* #page-wrapper { 2129 | @include vendor('transform', 'translateX(#{_size(navPanel)})'); 2130 | }*/ 2131 | 2132 | #titleBar { 2133 | @include vendor('transform', 'translateX(#{_size(navPanel)})'); 2134 | } 2135 | 2136 | #navPanel { 2137 | @include vendor('transform', 'translateX(0)'); 2138 | } 2139 | } 2140 | 2141 | /* Footer */ 2142 | 2143 | #footer { 2144 | padding: $size-wrapper-pad-tb $size-wrapper-pad-lr; 2145 | } 2146 | 2147 | } 2148 | 2149 | /* XSmall */ 2150 | 2151 | @include breakpoint('<=xsmall') { 2152 | 2153 | $size-wrapper-pad-tb: 3em; 2154 | $size-wrapper-pad-lr: 1.25em; 2155 | 2156 | /* Basic */ 2157 | 2158 | html, body { 2159 | min-width: 320px; 2160 | } 2161 | 2162 | body, input, select, textarea { 2163 | font-size: 12pt; 2164 | } 2165 | 2166 | /* Button */ 2167 | 2168 | input[type="submit"], 2169 | input[type="reset"], 2170 | input[type="button"], 2171 | .button { 2172 | padding: 0; 2173 | } 2174 | 2175 | /* Spotlight */ 2176 | 2177 | .spotlight { 2178 | .image.main { 2179 | max-height: 50vh; 2180 | } 2181 | 2182 | .content { 2183 | padding: $size-wrapper-pad-tb $size-wrapper-pad-lr ($size-wrapper-pad-tb - _size(element-margin)) $size-wrapper-pad-lr !important; 2184 | } 2185 | } 2186 | 2187 | /* Wrapper */ 2188 | 2189 | .wrapper { 2190 | padding: $size-wrapper-pad-tb $size-wrapper-pad-lr ($size-wrapper-pad-tb - _size(element-margin)) $size-wrapper-pad-lr; 2191 | } 2192 | 2193 | /* Banner */ 2194 | 2195 | #banner { 2196 | .content { 2197 | padding: $size-wrapper-pad-tb ($size-wrapper-pad-lr * 1.25) ($size-wrapper-pad-tb * 1.75) ($size-wrapper-pad-lr * 1.25); 2198 | } 2199 | } 2200 | 2201 | /* Footer */ 2202 | 2203 | #footer { 2204 | padding: $size-wrapper-pad-tb $size-wrapper-pad-lr; 2205 | 2206 | .copyright { 2207 | line-height: inherit; 2208 | 2209 | li { 2210 | border-left: 0; 2211 | display: block; 2212 | margin: 0; 2213 | padding: 0; 2214 | } 2215 | } 2216 | } 2217 | 2218 | } -------------------------------------------------------------------------------- /demo/client/website/src/assets/scss/noscript.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/vendor'; 5 | @import 'libs/breakpoints'; 6 | @import 'libs/html-grid'; 7 | 8 | /* 9 | Landed by HTML5 UP 10 | html5up.net | @ajlkn 11 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 12 | */ 13 | 14 | /* Loader */ 15 | 16 | body.landing { 17 | 18 | &.is-preload { 19 | 20 | // Spinner (active) 21 | 22 | &:before { 23 | display: none; 24 | } 25 | 26 | // Overlay (active) 27 | 28 | &:after { 29 | display: none; 30 | } 31 | 32 | } 33 | } -------------------------------------------------------------------------------- /demo/client/website/src/assets/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /demo/client/website/src/assets/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /demo/client/website/src/assets/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /demo/client/website/src/assets/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /demo/client/website/src/assets/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /demo/client/website/src/assets/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /demo/client/website/src/assets/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /demo/client/website/src/assets/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /demo/client/website/src/assets/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /demo/client/website/src/assets/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /demo/client/website/src/assets/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /demo/client/website/src/assets/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/demo/client/website/src/assets/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /demo/client/website/src/components/Banner.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Elucid_Logo from '../assets/images/Elucid_Logo1.jpg' 3 | import { Link as ScrollLink } from 'react-scroll' 4 | 5 | // first part of the home page component 6 | const Banner = props => ( 7 | 32 | ); 33 | 34 | export default Banner 35 | -------------------------------------------------------------------------------- /demo/client/website/src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | //Last part of the home page 4 | const Footer = props => ( 5 | 45 | ) 46 | 47 | export default Footer 48 | -------------------------------------------------------------------------------- /demo/client/website/src/components/Graphiql.jsx: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import GraphiQL from 'graphiql'; 4 | import 'graphiql/graphiql.min.css'; 5 | 6 | const Graphiql = () => ( 7 |
8 | { 10 | const data = await fetch('https://elucid-graphiql.herokuapp.com/graphql', { 11 | method: 'POST', 12 | headers: { 13 | Accept: 'application/json', 14 | 'Content-Type': 'application/json', 15 | }, 16 | body: JSON.stringify(graphQLParams), 17 | credentials: 'same-origin', 18 | }); 19 | return data.json().catch(() => data.text()); 20 | }} 21 | /> 22 |
23 | ); 24 | 25 | export default Graphiql; -------------------------------------------------------------------------------- /demo/client/website/src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby' 3 | import MobileMenu from './MobileMenu' 4 | 5 | const timeoutLength = 300 6 | 7 | class Header extends React.Component { 8 | constructor(props) { 9 | super(props) 10 | this.state = { 11 | active: false, 12 | mobileActiveClass: '', 13 | mouseOverButton: false, 14 | mouseOverMenu: false, 15 | mouseOverSubButton: false, 16 | mouseOverSubMenu: false, 17 | } 18 | } 19 | 20 | toggleHamburger = () => { 21 | this.setState( 22 | { 23 | active: !this.state.active, 24 | }, 25 | () => { 26 | // set the class for the mobile menu 27 | this.state.active 28 | ? this.setState({ 29 | mobileActiveClass: 'navPanel-visible', 30 | }) 31 | : this.setState({ 32 | mobileActiveClass: '', 33 | }) 34 | } 35 | ) 36 | } 37 | 38 | handleMouseHover() { 39 | this.setState(this.toggleHoverState) 40 | } 41 | 42 | toggleHoverState(state) { 43 | return { 44 | isHovering: !state.isHovering, 45 | } 46 | } 47 | 48 | enterButton = () => { 49 | this.setState({ mouseOverButton: true }) 50 | } 51 | 52 | leaveButton = () => { 53 | setTimeout(() => { 54 | this.setState({ mouseOverButton: false }) 55 | }, timeoutLength) 56 | } 57 | 58 | enterMenu = () => { 59 | this.setState({ mouseOverMenu: true }) 60 | } 61 | 62 | leaveMenu = () => { 63 | setTimeout(() => { 64 | this.setState({ mouseOverMenu: false }) 65 | }, timeoutLength) 66 | } 67 | 68 | enterSubButton = () => { 69 | this.setState({ mouseOverSubButton: true }) 70 | } 71 | 72 | leaveSubButton = () => { 73 | setTimeout(() => { 74 | this.setState({ mouseOverSubButton: false }) 75 | }, timeoutLength) 76 | } 77 | 78 | enterSubMenu = () => { 79 | this.setState({ mouseOverSubMenu: true }) 80 | } 81 | 82 | leaveSubMenu = () => { 83 | setTimeout(() => { 84 | this.setState({ mouseOverSubMenu: false }) 85 | }, timeoutLength) 86 | } 87 | 88 | render() { 89 | const siteTitle = this.props.siteTitle 90 | const menuLinks = this.props.menuLinks 91 | const open = this.state.mouseOverButton || this.state.mouseOverMenu 92 | const subOpen = this.state.mouseOverSubButton || this.state.mouseOverSubMenu 93 | 94 | return ( 95 | 96 |
97 | 107 | 110 |
111 | 112 | 240 |
241 | ) 242 | } 243 | } 244 | 245 | export default Header 246 | -------------------------------------------------------------------------------- /demo/client/website/src/components/Home1.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import pic02 from '../assets/images/pic02.jpg' 3 | import { Link as ScrollLink } from 'react-scroll' 4 | import Fade from 'react-reveal/Fade' 5 | 6 | const One = props => ( 7 |
12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 |
20 |
21 |

Why Elucid?

22 |

23 | GraphQL is awesome, but it's not without its complications. 24 |

25 |
26 |
27 |
28 |
29 |
30 |
31 | 40 | Next 41 | 42 |
43 | ) 44 | export default One 45 | -------------------------------------------------------------------------------- /demo/client/website/src/components/Home2.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import pic03 from '../assets/images/pic03.gif' 3 | import { Link as ScrollLink } from 'react-scroll' 4 | import Fade from 'react-reveal/Fade' 5 | 6 | const Two = props => ( 7 |
12 | 13 | 14 | 15 | 16 |
17 |
18 |

Because GraphQL replaces the explicit architecture of a RESTful API, it increases the conceptual complexity for a developer interacting with it.

19 |
20 |

21 | In addition to lacking out-of-the-box HTTP status code manipulation, error messaging can also become vague and ambiguous. 22 |

23 |

It’s not always clear when a null response field is the result of a queried-for data point simply not existing in the server, versus being the result of a malformed query or even an internal server error on the part of the GraphQL installation. 24 |

25 |
26 |
27 | 36 | Next 37 | 38 |
39 | ) 40 | 41 | export default Two 42 | -------------------------------------------------------------------------------- /demo/client/website/src/components/Home3.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import pic04 from '../assets/images/pic04.gif' 3 | import { Link as ScrollLink } from 'react-scroll' 4 | import Fade from 'react-reveal/Fade' 5 | 6 | const Three = props => ( 7 |
12 | 13 | 14 | 15 | 16 |
17 |
18 |

Elucid is an extension of graphql.js that improves GraphQL's error messaging and status code handling.

19 |
20 |

21 | It's lightweight and easy to get off the ground. It's not an entire framework for developers to adopt, just something to make their lives a little easier. 22 |

23 |

24 | We believe Elucid is the most essential, minimalist error handling library for Javascript implementations of GraphQL available today. We hope you’ll give it a try and find that it helps illuminate the path forward in your own projects. 25 |

26 | 33 |
34 |
35 | 44 | Next 45 | 46 |
47 | ) 48 | 49 | export default Three 50 | -------------------------------------------------------------------------------- /demo/client/website/src/components/Legal.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Fade from 'react-reveal/Fade' 3 | 4 | const Legal = props => ( 5 |
6 | 7 |
8 |
9 |

This project is under ISC License.

10 |
11 | 12 | Get Elucid 13 | 14 |
15 |
16 |
17 | ) 18 | export default Legal 19 | -------------------------------------------------------------------------------- /demo/client/website/src/components/MobileMenu.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby' 3 | 4 | const MobileMenu = props => ( 5 | 57 | ) 58 | 59 | export default MobileMenu 60 | -------------------------------------------------------------------------------- /demo/client/website/src/components/TeamMember.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | 4 | const TeamMember = (props) => { 5 | const { src, bio, name, linkedin, github } = props; 6 | 7 | return ( 8 |
9 | Elucid Team 10 |

{name}

11 |

{bio}

12 | 24 |
25 | ); 26 | }; 27 | 28 | export default TeamMember; 29 | -------------------------------------------------------------------------------- /demo/client/website/src/components/layout.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { StaticQuery, graphql } from 'gatsby' 3 | import '../assets/scss/main.scss' 4 | import Header from './Header' 5 | import Footer from './Footer' 6 | 7 | const Layout = ({ children, ...props }) => ( 8 | ( 29 | 30 |
31 |
32 |
36 | {children} 37 |
38 |
39 |
40 |
41 | )} 42 | /> 43 | ) 44 | 45 | export default Layout 46 | -------------------------------------------------------------------------------- /demo/client/website/src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Layout from '../components/layout' 3 | 4 | const NotFoundPage = () => ( 5 | 6 |
7 |
8 |
9 |

NOT FOUND

10 |

You just hit a route that doesn't exist... the sadness.

11 |
12 |
13 |
14 |
15 | ) 16 | 17 | export default NotFoundPage 18 | -------------------------------------------------------------------------------- /demo/client/website/src/pages/demo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Helmet } from 'react-helmet' 3 | import Layout from '../components/layout' 4 | import pic01 from '../assets/images/Elucid-Before-And-After-14.png' 5 | import pic02 from '../assets/images/Elucid-Before-And-After-23.png' 6 | import pic03 from '../assets/images/Elucid-Before-And-After-Too-Many.png' 7 | import Graphiql from '../components/Graphiql' 8 | import {CopyToClipboard} from 'react-copy-to-clipboard'; 9 | 10 | const query1 = '{\n person { \n name \n } \n}' 11 | const query2 = '{\n person (id: 1) { \n name \n } \n}' 12 | const query3 = '{\n Person2: person (id:2) {\n name\n gender\n } \n Person1: person (id:9) {\n name\n }\n \n person {\n name\n }\n}' 13 | 14 | const Demo = props => ( 15 | 16 | 17 | Demo - Elucid 18 | 19 | 20 | 21 |
22 |
23 |
24 |

Lighting the Way

25 |

26 | Let's take a closer look at some of what Elucid can do. 27 |

28 |
29 |
30 |
31 | 57 |
58 |
59 |
60 |
61 | 62 |
63 |
64 |
65 |
66 |
67 | 68 |
69 |
70 |
71 | 92 |
93 |
94 |
95 |
96 | 97 |
98 |
99 |
100 |
101 |
102 | 103 |
104 |
105 |
106 | 126 |
127 |
128 |
129 |
130 | 131 |
132 |
133 |
134 |
135 |
136 | 137 |
138 |
139 |

Graphiql Demo

140 |

141 | Test Elucid out yourself! Copy and paste sample code from the 142 | examples above, or plug in your own. 143 |

144 |
145 | 146 |
147 |
148 |
149 | ) 150 | 151 | export default Demo 152 | -------------------------------------------------------------------------------- /demo/client/website/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Helmet } from 'react-helmet' 3 | import Layout from '../components/layout' 4 | import Banner from '../components/Banner' 5 | import One from '../components/Home1' 6 | import Two from '../components/Home2' 7 | import Three from '../components/Home3' 8 | import Legal from '../components/Legal' 9 | 10 | 11 | class Home extends React.Component { 12 | render() { 13 | return ( 14 | 15 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ) 30 | } 31 | }; 32 | 33 | export default Home 34 | -------------------------------------------------------------------------------- /demo/client/website/src/pages/team.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Helmet from 'react-helmet' 3 | import Layout from '../components/layout' 4 | import TeamMember from '../components/TeamMember' 5 | import pic07 from '../assets/images/pic07.gif' 6 | import Khayal from '../assets/images/team_photos/Khayal.jpg' 7 | import Vivian from '../assets/images/team_photos/Vivian.jpg' 8 | import Spencer from '../assets/images/team_photos/Spencer.jpg' 9 | import Gary from '../assets/images/team_photos/Gary.jpg' 10 | 11 | 12 | const khayal = { 13 | name: 'Khayal Alasgarov', 14 | src: Khayal, 15 | bio: 16 | 'Khayal is a full-stack software engineer specializing in React, Express, and relational databases with a focus in front-end performance optimization and server-side data transfer protocols and has a strong interest in GraphQL. Outside of coding, he is a travel and hospitality enthusiast and enjoys drawing abstract art.', 17 | linkedin: 'https://www.linkedin.com/in/khayal-alasgaroff/', 18 | github: 'https://github.com/Alasgaroff', 19 | }; 20 | 21 | const vivian = { 22 | name: 'Vivian Wu', 23 | src: Vivian, 24 | bio: 25 | 'Vivian is a software engineer who enjoys technology and learning, specializing in JavaScript and Express, and is experienced in building full stack applications with an emphasis on code maintainability and optimization. Her interests outside of coding include eating, exploring Big Sur, going wine tasting, and training/competing in dog sports with her pup Merlin.', 26 | linkedin: 'https://www.linkedin.com/in/viv-wu/', 27 | github: 'https://github.com/vw-in-github', 28 | }; 29 | 30 | const spencer = { 31 | name: 'Spencer Szu', 32 | src: Spencer, 33 | bio: 34 | 'Spencer is a full-stack software engineer specializing in React, Node/Express, and SQL relational databases with a passion for learning new concepts and technologies. His strong interest in GraphQL makes him a great contributer to Elucid. Outside of coding, Spencer enjoys weightlifting, investing, traveling and is a music enthusiast.', 35 | linkedin: 'https://www.linkedin.com/in/spencer-szu', 36 | github: 'https://github.com/Szuay', 37 | }; 38 | 39 | const gary = { 40 | name: 'Giovanni Iacobucci', 41 | src: Gary, 42 | bio: 43 | "Giovanni crafts interactive media experiences, bringing a holistic full-stack approach to a deeply user-focused point of view. He's also a visual designer, filmmaker and video editor, and writer.", 44 | linkedin: 'https://www.linkedin.com/in/gioiacobucci', 45 | github: 'https://github.com/garyiacobucci', 46 | }; 47 | 48 | 49 | const Team = props => ( 50 | 51 | 52 | Team Elucid 53 | 54 | 55 | 56 |
57 |
58 |
59 |

Contributors

60 |

61 | Meet team Elucid 62 |

63 |
64 | 65 |
66 |
67 |
68 | 75 | 82 | 89 | 96 |
97 |
98 |
99 |
100 | ) 101 | 102 | export default Team 103 | -------------------------------------------------------------------------------- /demo/server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /demo/server/Procfile: -------------------------------------------------------------------------------- 1 | web:node server.js -------------------------------------------------------------------------------- /demo/server/database/starwars_db_hardCoded.js.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | People: [ { 3 | id: 1, 4 | name: "Luke Skywalker", 5 | height: "172", 6 | mass: "77", 7 | hair_color: "blond", 8 | skin_color: "fair", 9 | eye_color: "blue", 10 | birth_year: "19BBY", 11 | gender: "male", 12 | homeworld: {name: "Tatooine", climate: "arid", population: "200000"}, 13 | }, 14 | { 15 | id: 2, 16 | // name: "Darth Vader", 17 | height: "202", 18 | mass: "136", 19 | hair_color: "none", 20 | skin_color: "white", 21 | eye_color: "yellow", 22 | birth_year: "41.9BBY", 23 | // gender: "male", 24 | homeworld: {name: "Tatooine", /*climate: "arid",*/ population: "200000"}, 25 | }, 26 | { 27 | id: 3, 28 | name: "Leia Organa", 29 | height: "150", 30 | mass: "49", 31 | hair_color: "brown", 32 | skin_color: "light", 33 | eye_color: "brown", 34 | birth_year: "19BBY", 35 | gender: "female", 36 | homeworld: {name: "Alderaan", climate: "temperate", population: "2000000000"}, 37 | }, 38 | 39 | { 40 | id: 4, 41 | name: "Owen Lars", 42 | height: "178", 43 | mass: "120", 44 | hair_color: "brown, grey", 45 | skin_color: "light", 46 | eye_color: "blue", 47 | birth_year: "52BBY", 48 | gender: "male", 49 | homeworld: {name: "Tatooine", climate: "arid", population: "200000"}, 50 | }, 51 | 52 | { 53 | id: 5, 54 | name: "Biggs Darklighter", 55 | mass: "145lbs", 56 | hair_color: "red", 57 | skin_color: "white", 58 | birth_year: "07.31.2021", 59 | gender: "male", 60 | species_id: 31, 61 | homeworld_id: {name: "Tatooine", climate: "arid", population: "200000"}, 62 | height: 150, 63 | }, 64 | ], 65 | } -------------------------------------------------------------------------------- /demo/server/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Elucid 6 | 7 | 8 | Document 9 | 10 | 11 | 12 |

THIS IS INDEX HTML FILE

13 | 14 | 15 | -------------------------------------------------------------------------------- /demo/server/models/model.js: -------------------------------------------------------------------------------- 1 | const { Pool } = require('pg'); 2 | const yourURI = require ("./yourURI.js"); 3 | 4 | const myURI = yourURI; 5 | 6 | // UNCOMMENT THE LINE BELOW IF USING POSTGRESQL 7 | const URI = process.env.PG_URI || myURI; 8 | 9 | const pool = new Pool({ 10 | connectionString: URI, 11 | }); 12 | 13 | // <-- export your model 14 | module.exports = { 15 | query: (text, params, callback) => { 16 | console.log('executed query', text); 17 | return pool.query(text, params, callback); 18 | }, 19 | }; -------------------------------------------------------------------------------- /demo/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "1.0.1", 4 | "description": "", 5 | "main": "index.html", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node ./server.js", 9 | "dev": "nodemon ./server.js" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/oslabs-beta/Elucid.git" 14 | }, 15 | "author": "", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/oslabs-beta/Elucid/issues" 19 | }, 20 | "homepage": "https://github.com/oslabs-beta/Elucid#readme", 21 | "dependencies": { 22 | "body-parser": "^1.19.0", 23 | "cors": "^2.8.5", 24 | "elucid.js": "^1.0.0", 25 | "express": "^4.17.1", 26 | "express-graphql": "^0.12.0", 27 | "graphql": "^15.5.1", 28 | "path": "^0.12.7" 29 | }, 30 | "devDependencies": { 31 | "eslint": "^7.31.0", 32 | "eslint-config-airbnb": "^18.2.1", 33 | "nodemon": "^2.0.12", 34 | "heroku": "^7.57.0" 35 | }, 36 | "engines": { 37 | "node": "14.x", 38 | "npm": "6.14.7" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /demo/server/schema/resolvers.js: -------------------------------------------------------------------------------- 1 | // The resolvers provides a resolver function for each API endpoint 2 | const db = require('../database/starwars_db_hardCoded.js'); 3 | 4 | const resolvers = { 5 | people: () => { 6 | return db.People; 7 | }, 8 | person: ({ id } ) => { 9 | return db.People.find((person) => person.id == id); 10 | }, 11 | }; 12 | 13 | module.exports = resolvers; 14 | -------------------------------------------------------------------------------- /demo/server/schema/schema.js: -------------------------------------------------------------------------------- 1 | // const graphqlNodeModule = 2 | // process.env.NODE_ENV === 'development' 3 | // ? '../../../elucid/node_modules/graphql' 4 | // : 'graphql'; 5 | 6 | // const { buildSchema } = require(graphqlNodeModule); 7 | const { buildSchema } = require('graphql'); 8 | // Construct a schema, using GraphQL schema language 9 | 10 | const schema = buildSchema(` 11 | type Homeworld { 12 | name: String 13 | climate: String 14 | population: String 15 | } 16 | 17 | type Person { 18 | id: Int! 19 | name: String 20 | height: String! 21 | mass: String! 22 | hair_color: String! 23 | skin_color: String! 24 | eye_color: String! 25 | birth_year: String! 26 | gender: String 27 | homeworld: Homeworld! 28 | } 29 | 30 | type Query { 31 | people: [Person!] 32 | person(id: Int): Person 33 | } 34 | `); 35 | 36 | module.exports = schema; -------------------------------------------------------------------------------- /demo/server/server.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | const express = require('express'); 3 | const path = require('path'); 4 | const schema = require('./schema/schema'); 5 | const resolvers = require('./schema/resolvers'); 6 | const elucid = require('elucid.js'); 7 | const cors = require('cors') 8 | 9 | const PORT = process.env.PORT || 5000; 10 | 11 | const app = express(); 12 | 13 | const corsOption = { 14 | options: 'https://elucid.dev' 15 | } 16 | 17 | app.use(express.json()); 18 | 19 | // Handle requests to GraphQL endpoint 20 | app.use('/graphql', cors(corsOption), elucid({ 21 | schema, 22 | resolvers, 23 | graphiql: true, 24 | pretty: true, 25 | })); 26 | 27 | // Fetch index page: 28 | app.use('/', (req, res) => { 29 | return res.status(200).sendFile(path.resolve(__dirname, 'index.html')); 30 | }); 31 | 32 | // If endpoint is not found: 33 | app.use((req, res) => res.status(404).send('Page not found')); 34 | 35 | // Global error handler fallback: 36 | app.use((err, req, res, next) => { 37 | const defErr = { 38 | log: 'sent to the global error handler', 39 | status: 500, 40 | msg: { err: 'error in server' }, 41 | }; 42 | const errorObj = Object.assign(defErr, err); 43 | console.log(errorObj.log); 44 | return res.status(errorObj.status).json(errorObj.msg); 45 | }); 46 | 47 | // Listening on port 3000: 48 | app.listen(PORT, () => { 49 | console.log(`server listening on PORT: ${PORT}`); 50 | }); 51 | 52 | module.exports = app; 53 | -------------------------------------------------------------------------------- /elucid/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json -------------------------------------------------------------------------------- /elucid/.npmignore: -------------------------------------------------------------------------------- 1 | __test__ 2 | .gitignore 3 | assets -------------------------------------------------------------------------------- /elucid/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | [![License: ISC](https://img.shields.io/badge/license-ISC-yellow.svg)](https://opensource.org/licenses/ISC) 4 | ![AppVeyor](https://img.shields.io/badge/build-passing-brightgreen.svg) 5 | ![AppVeyor](https://img.shields.io/badge/version-1.0.2-blue.svg) 6 | [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/oslabs-beta/Elucid/blob/main/CONTRIBUTING.md) 7 | 8 | # Elucid 9 | 10 | Elucid: Added Insight for GraphQL. 11 | 12 | Accelerated by [OS Labs](https://github.com/oslabs-beta/) and developed by [Khayal Alasgarov](https://github.com/Alasgaroff), [Vivian Wu](https://github.com/vw-in-github), [Spencer Szu](https://github.com/Szuay), and [Giovanni Iacobucci](https://github.com/garyiacobucci). 13 | 14 | ## Features 15 | 16 | - Extensible error detection and messaging for common graphQL issues deriving from server problems and bad queries. 17 | 18 | ## Installation 19 | 20 | Elucid can be installed as an npm package: 21 | 22 | - Download Elucid from npm in your terminal with `npm i elucid.js` 23 | 24 | ### Installing and connecting with Express: 25 | 26 | In its current form, Elucid extends express-graphql's server middleware, so you'll need your GraphQL HTTP server setup running Express (or a similar web framework that supports connect styled middleware, such as Connect or Restify) as a prerequisite. Please see the [express documentation](https://github.com/expressjs/express/blob/master/Readme.md) for more information on how to use the express framework. 27 | 28 | ### Usage notes 29 | 30 | - Currently, Elucid is in beta release and adds additional insight and status code manipulation for simple queries. 31 | - We intend to expand support in future releases to include multiple arguments, directives, variables, mutations and subscriptions. 32 | 33 | ## Getting started with Elucid 34 | 35 | Here's an example of a typical server.js setup running Elucid: 36 | 37 | ```js 38 | const express = require('express'); 39 | const path = require('path'); 40 | const schema = require('./schema/schema'); 41 | const resolvers = require('./schema/resolvers'); 42 | const elucid = require('elucid.js'); 43 | 44 | const PORT = 3000; 45 | 46 | const app = express(); 47 | 48 | app.use(express.json()); 49 | 50 | // Handle requests to GraphQL endpoint 51 | app.use('/graphql', elucid({ 52 | schema, 53 | resolvers, 54 | graphiql: true, 55 | pretty: true, 56 | })); 57 | ``` 58 | 59 | - Notice that after installation of its npm package, the Elucid function must be imported into your server file as a required module, 'elucid.js'. 60 | - Then, the elucid function is passed into the Express 'use' method as its second argument. 61 | - This is very similar to how express-graphql's 'graphqlHTTP' middleware function is used. In fact, the configuration object that elucid() takes as an argument passes its values through to an invocation of graphqlHTTP. That includes the keys 'schema' and 'resolvers' (note that these evaluate to your imported GraphQL schema and resolver files), as well as the config booleans 'graphiql' and 'pretty'. 62 | - At runtime, elucid() will kick off a sequence of events under the hood. GraphqlHTTP will process the request. If it doesn't catch an error on its own, Elucid's functionality will kick in to do its additional post-processing, which may include modifying the default response status code and returning a more detailed, useful context message in the 'elucid' property of the response object's 'extensions' property. 63 | 64 | ### Contribute to Elucid 65 | 66 | Interested in making a contribution to Elucid? [Click](https://github.com/oslabs-beta/Elucid/blob/main/CONTRIBUTING.md) for our open-source contribution guidelines. 67 | 68 | Happy bug hunting/data fetching! 69 | Team Elucid -------------------------------------------------------------------------------- /elucid/__test__/handleResultWithError.test.js: -------------------------------------------------------------------------------- 1 | const handleResultWithError = require('../../../elucid/elucidpkg/src/helpers/handleResultWithError'); 2 | 3 | describe('tests for response with error and data keys', () => { 4 | const context = { 5 | response: { 6 | statusCode: 200, 7 | }, 8 | }; 9 | 10 | it('error message includes non-nullable, should receive message to check errors.message', () => { 11 | const errorsArray = [ 12 | { 13 | message: 'Cannot return null for non-nullable field Person.homeworld.', 14 | locations: [ 15 | { 16 | line: 5, 17 | column: 3, 18 | }, 19 | ], 20 | path: ['people', 4, 'homeworld'], 21 | }, 22 | ]; 23 | 24 | expect(handleResultWithError(errorsArray, context)).toEqual({ 25 | message: 'Check errors.message', 26 | }); 27 | }); 28 | 29 | it('error message does not include non-nullable, should return object with message string Server error and to check your resolvers', () => { 30 | const errorsArray = [ 31 | { 32 | message: 'id is not defined', 33 | locations: [ 34 | { 35 | line: 2, 36 | column: 3, 37 | }, 38 | ], 39 | path: ['person'], 40 | }, 41 | ]; 42 | 43 | expect(handleResultWithError(errorsArray, context)).toEqual({ 44 | message: 'Server error: please check your resolvers', 45 | statusCode: 500, 46 | }); 47 | }); 48 | 49 | it('no error message, should return object with message string confirming validation and statusCode 200', () => { 50 | expect(handleResultWithError(undefined, context)).toBeNull(); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /elucid/assets/Elucid_Lamp_with_Text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/elucid/assets/Elucid_Lamp_with_Text.png -------------------------------------------------------------------------------- /elucid/assets/server-setup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Elucid/8fdb334119879c04c971a7123c829a7c2bfa7aff/elucid/assets/server-setup.jpg -------------------------------------------------------------------------------- /elucid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elucid.js", 3 | "version": "1.0.2", 4 | "description": "Elucid is an open-source NPM package providing developers with a lightweight solution to reduce ambiguity in GraphQL responses and HTTP headers. Use Elucid for better error-handling, testing, and debugging experiences.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "jest" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/oslabs-beta/Elucid/tree/main/elucid" 12 | }, 13 | "keywords": [ 14 | "graphQL", 15 | "error", 16 | "errorhandling", 17 | "elucid", 18 | "http", 19 | "express", 20 | "middleware" 21 | ], 22 | "author": "Elucid", 23 | "license": "ISC", 24 | "contributors": [ 25 | { 26 | "name": "Vivian Wu", 27 | "url": "https://github.com/vw-in-github" 28 | }, 29 | { 30 | "name": "Khayal Alasgarov", 31 | "url": "https://github.com/Alasgaroff" 32 | }, 33 | { 34 | "name": "Gary Iacobucci", 35 | "url": "https://github.com/garyiacobucci" 36 | }, 37 | { 38 | "name": "Spencer Szu", 39 | "url": "https://github.com/Szuay" 40 | } 41 | ], 42 | "homepage": "https://elucid.dev", 43 | "dependencies": { 44 | "express-graphql": "^0.12.0", 45 | "graphql": "^14.7.0 || ^15.3.0" 46 | }, 47 | "devDependencies": { 48 | "jest": "^27.0.6", 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /elucid/src/helpers/buildElucidMessage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Main function to build elucid message output, by invoking helper functions buildQueryFieldsWithArgsMsg buildSchemaFieldsWithNullableArgsMsg, buildDataNotFoundMsg 3 | * @param {Object} responseFieldsWithNull result of invoking getResponseFieldsWithNull function, which returns an Object, keys are response fields with null values, values are boolen true 4 | * @param {Object} queryFieldsWithArgs result of invoking getQueryFieldsWithArgs function, which returns an Object, keys are alias or query field names, values are the arguments value 5 | * @param {Object} schemaFieldsWithNullableArgs result of invoking getSchemaFieldsWithNullableArgs, which returns an Object, keys are field names of queries in the schema, values are array of argument(s) of type strings not marked as non-nullable 6 | * @returns object with message key with value of type string 7 | */ 8 | function buildElucidMessage( 9 | responseFieldsWithNull, 10 | queryFieldsWithArgs, 11 | schemaFieldsWithNullableArgs 12 | ) { 13 | //if no null fields, return message to confirm validation 14 | //if there are null fields, group them by checking if field is in queryFieldsWithArg or schemaFieldsWithNullable Args 15 | //and return all applicable messages 16 | if (Object.keys(responseFieldsWithNull).length === 0) 17 | return { 18 | message: 'Elucid validated your response. No errors found.', 19 | }; 20 | else { 21 | const queryFieldsWithArgsMessage = buildQueryFieldsWithArgsMsg( 22 | queryFieldsWithArgs, 23 | responseFieldsWithNull 24 | ); 25 | 26 | const schemaFieldsWithNullableArgsMessage = 27 | buildSchemaFieldsWithNullableArgsMsg( 28 | schemaFieldsWithNullableArgs, 29 | responseFieldsWithNull 30 | ); 31 | 32 | const dataNotFoundMessage = buildDataNotFoundMsg(responseFieldsWithNull); 33 | 34 | return { 35 | message: `${dataNotFoundMessage}${ 36 | dataNotFoundMessage && queryFieldsWithArgsMessage 37 | ? ' ' + queryFieldsWithArgsMessage 38 | : queryFieldsWithArgsMessage 39 | }${ 40 | (dataNotFoundMessage || queryFieldsWithArgsMessage) && 41 | schemaFieldsWithNullableArgsMessage 42 | ? ' ' + schemaFieldsWithNullableArgsMessage 43 | : schemaFieldsWithNullableArgsMessage 44 | }`, 45 | }; 46 | } 47 | 48 | function buildQueryFieldsWithArgsMsg(queryFields, responseFields) { 49 | let queryFieldsWithArgsMsg = ''; 50 | const result = []; 51 | 52 | for (const field in queryFields) { 53 | if (responseFields[field]) { 54 | if (result.length === 0) result.push(`Query for: "${field}(${queryFields[field]})"`); 55 | else result.push(` "${field}(${queryFields[field]})"`) 56 | delete responseFields[field]; 57 | } 58 | } 59 | 60 | if (result.length > 0) { 61 | result.push(` cannot be found & may not exist; if this is an error, contact your server admin.`) 62 | queryFieldsWithArgsMsg += `${result}`; 63 | } 64 | 65 | return queryFieldsWithArgsMsg; 66 | } 67 | 68 | function buildSchemaFieldsWithNullableArgsMsg(schemaFields, responseFields) { 69 | let schemaFieldsWithNullableArgsMsg = ''; 70 | const result = []; 71 | 72 | for (const field in schemaFields) { 73 | if (responseFields[field]) { 74 | if (result.length === 0) result.push(`Query for: "${field}" did not have arg(s) of "${schemaFields[field]}"`); 75 | else result.push(` "${field}" did not have arg(s) of "${schemaFields[field]}"`) 76 | delete responseFields[field]; 77 | } 78 | } 79 | 80 | if (result.length > 0) { 81 | result.push(` verify if arg(s) should be NON-NULLABLE in schema.`) 82 | schemaFieldsWithNullableArgsMsg += `${result}`; 83 | } 84 | 85 | return schemaFieldsWithNullableArgsMsg; 86 | } 87 | 88 | function buildDataNotFoundMsg(responseFields) { 89 | let dataNotFoundMsg = ''; 90 | 91 | if (Object.keys(responseFields).length > 0) { 92 | dataNotFoundMsg += `Data not found in field(s): ${Object.keys( 93 | responseFields 94 | )}.`; 95 | } 96 | return dataNotFoundMsg; 97 | } 98 | } 99 | 100 | module.exports = buildElucidMessage; 101 | -------------------------------------------------------------------------------- /elucid/src/helpers/elucidateHelper.js: -------------------------------------------------------------------------------- 1 | const buildElucidMessage = require('./buildElucidMessage'); 2 | const getSchemaFieldsWithNullableArgs = require('./getSchemaFieldsWithNullableArgs'); 3 | const getQueryFieldsWithArgs = require('./getQueryFieldsWithArgs'); 4 | const getResponseFieldsWithNull = require('./getResponseFieldsWithNull'); 5 | 6 | /** 7 | * Main entry point for invocation of helper functions 8 | * @param {Object} data GraphQL response object's value at 'data' 9 | * @param {Object} context Contains response and request Object 10 | * @param {GraphQL schema} schema export of user's schema, built using buildSchema method of GraphQL 11 | * @returns invocation of buildElucidMessage function, which returns an object with message key with value of type string and statusCode key with value of type integer 12 | */ 13 | function elucidateHelper(data, context, schema) { 14 | let queryFieldsWithArgs; 15 | 16 | const responseFieldsWithNull = getResponseFieldsWithNull(data); 17 | 18 | //if there are null fields, provide corresponding msg 19 | if (Object.keys(responseFieldsWithNull).length > 0) { 20 | const { query } = context.request.body; 21 | queryFieldsWithArgs = getQueryFieldsWithArgs(query); 22 | } 23 | 24 | //schemaFieldsWithNullableArgs is an object with array values 25 | const schemaFieldsWithNullableArgs = getSchemaFieldsWithNullableArgs(schema); 26 | 27 | return buildElucidMessage( 28 | responseFieldsWithNull, 29 | queryFieldsWithArgs, 30 | schemaFieldsWithNullableArgs 31 | ); 32 | } 33 | 34 | module.exports = elucidateHelper; 35 | -------------------------------------------------------------------------------- /elucid/src/helpers/getQueryFieldsWithArgs.js: -------------------------------------------------------------------------------- 1 | const { parse } = require('graphql'); 2 | 3 | /** 4 | * getQueryFieldsWithArgs populates queryFieldsWithArgs object; currently supports queries with 1 argument 5 | * @param {Object[]} query object elements pertain to query fields in query string 6 | * @returns result object, keys are alias or query field names, values are the arguments value 7 | */ 8 | function getQueryFieldsWithArgs(query) { 9 | const parsedQuery = parse(query).definitions[0].selectionSet.selections; 10 | 11 | const result = {}; 12 | 13 | for (const obj of parsedQuery) { 14 | if (obj.arguments.length > 0) { 15 | if (obj.alias) 16 | result[ 17 | obj.alias.value 18 | ] = `${obj.arguments[0].name.value}: ${obj.arguments[0].value.value}`; 19 | else 20 | result[ 21 | obj.name.value 22 | ] = `${obj.arguments[0].name.value}: ${obj.arguments[0].value.value}`; 23 | } 24 | } 25 | return result; 26 | } 27 | 28 | module.exports = getQueryFieldsWithArgs; 29 | -------------------------------------------------------------------------------- /elucid/src/helpers/getResponseFieldsWithNull.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {Object} input graphQL response object's value at data key 3 | * @param {string} property key name for object value passed in on subsequent recursive calls 4 | * @param {Object} result stores all key names with value of null; currently supports storing key names nested up to 2 levels deep (key.subkey) 5 | * @returns result object, keys are response fields with null values, values are boolen true 6 | */ 7 | 8 | //helper function to get all response keys with null values 9 | function getResponseFieldsWithNull(input, property, result = {}) { 10 | for (const key in input) { 11 | // if key is null then we return an error 12 | if (input[key] === null) { 13 | if (property) result[`${property}.${key}`] = true; 14 | else result[`${key}`] = true; 15 | } 16 | // account for nested objects within keys 17 | else if (input[key] !== null && typeof input[key] === 'object') { 18 | getResponseFieldsWithNull(input[key], key, result); 19 | } 20 | } 21 | return result; 22 | } 23 | 24 | module.exports = getResponseFieldsWithNull; 25 | -------------------------------------------------------------------------------- /elucid/src/helpers/getSchemaFieldsWithNullableArgs.js: -------------------------------------------------------------------------------- 1 | const { introspectionFromSchema } = require('graphql'); 2 | 3 | /** 4 | * @param {GraphQL schema} schema export of user's schema, built using buildSchema method of GraphQL 5 | * @returns fields object, keys are field names of queries in the schema, values are array of argument(s) of type strings not marked as non-nullable 6 | */ 7 | function getSchemaFieldsWithNullableArgs(schema) { 8 | // introspectionFromSchema returns an object with information about schema 9 | // .__schema.types is an array of objects 10 | const typesFromSchema = introspectionFromSchema(schema).__schema.types; 11 | 12 | const schemaFields = (function (types) { 13 | const fields = {}; 14 | 15 | for (const type of types) { 16 | if (type.name === 'Query') { 17 | for (const queries of type.fields) { 18 | if (queries.args.length > 0) { 19 | for (const argument of queries.args) { 20 | if (argument.type.kind !== 'NON_NULL') { 21 | if (!fields[queries.name]) 22 | fields[queries.name] = [argument.name]; 23 | else fields[queries.name].push(argument.name); 24 | } 25 | } 26 | } 27 | } 28 | break; 29 | } 30 | } 31 | return fields; 32 | })(typesFromSchema); 33 | 34 | return schemaFields; 35 | } 36 | 37 | module.exports = getSchemaFieldsWithNullableArgs; 38 | -------------------------------------------------------------------------------- /elucid/src/helpers/handleResultWithError.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks for error object in a response with status code 200. Currently does not support mult query fields with a missing non-nullable argument 3 | * @param {Array} errorArr Response object's error value 4 | * @param {Object} context Contains request and response objects 5 | * @returns {(Object|null)} Returns object with message string or null 6 | */ 7 | 8 | function handleResultWithError(errorArr, context) { 9 | if (errorArr && !errorArr[0].message.includes('non-nullable field')) { 10 | context.response.statusCode = 500; 11 | return { 12 | message: `Server error: please check your resolvers`, 13 | statusCode: context.response.statusCode, 14 | }; 15 | } else if (errorArr) { 16 | return { 17 | message: `Check errors.message`, 18 | }; 19 | } 20 | return null; 21 | } 22 | 23 | module.exports = handleResultWithError; 24 | -------------------------------------------------------------------------------- /elucid/src/index.js: -------------------------------------------------------------------------------- 1 | const { graphqlHTTP } = require('express-graphql'); 2 | const elucidateHelper = require('./helpers/elucidateHelper'); 3 | const handleResultWithError = require('./helpers/handleResultWithError'); 4 | 5 | /** 6 | * 7 | * @param {Object} options sets settings for graphiql and pretty, and takes in user's GraphQL schema and resolvers 8 | * @returns elucidMiddleware function, which returns the invocation of graphqlHTTP method from express-graphql 9 | */ 10 | function elucid(options) { 11 | const { schema, resolvers, graphiql, pretty } = options; 12 | 13 | return function elucidMiddleware(request, response) { 14 | function elucidate(result, context, schema) { 15 | // 'Result' carries the GQL query result (result.data and result.errors) 16 | // 'context' carries http response/request object (context.response.statusCode / context.request.body ) 17 | const errorMsgExists = handleResultWithError(result.errors, context); 18 | 19 | if (errorMsgExists) return errorMsgExists; 20 | 21 | const { message } = elucidateHelper(result.data, context, schema); 22 | 23 | return { message, statusCode: context.response.statusCode }; 24 | } 25 | 26 | // Extensions variable is needed for 'extensions' property of graphqlHTTP 27 | // to work correctly. The callback contains invocation of 'elucidate' 28 | // 'elucidate' parses 200 OK responses to decide if additional 29 | // error handling is necessary 30 | const extensions = ({ result, context }) => { 31 | return { 32 | elucid: elucidate(result, context, schema), 33 | }; 34 | }; 35 | 36 | //customFormatErrorFn: (err) => {} can be used to define *additional* error-handling behavior for errors that Express-graphQL natively handles 37 | return graphqlHTTP({ 38 | schema, 39 | rootValue: resolvers, 40 | graphiql, 41 | pretty, 42 | context: { request, response }, 43 | extensions, 44 | })(request, response); 45 | }; 46 | } 47 | 48 | module.exports = elucid; 49 | --------------------------------------------------------------------------------