├── .eslintrc ├── .gitignore ├── .scss-lint.yml ├── .vscode └── cSpell.json ├── LICENSE ├── README.md ├── gatsby-config.js ├── package-lock.json ├── package.json ├── src ├── components │ ├── CodeBlock.js │ ├── CodepenLink.js │ ├── DownloadButton.js │ ├── DownloadLink.js │ ├── DownloadWhite.js │ ├── ImgCaption.js │ ├── Redirect.js │ ├── Term.js │ ├── TutorialItem.js │ ├── TutorialList.js │ ├── VideoPlayer.js │ ├── img │ │ ├── arrow-left.svg │ │ ├── arrow-left2.svg │ │ ├── arrow-right.svg │ │ ├── arrow-right2.svg │ │ ├── cssgrid.png │ │ ├── current.svg │ │ ├── debugger.png │ │ ├── done.svg │ │ ├── download-blue.svg │ │ ├── download.svg │ │ ├── dtgif │ │ │ ├── gif1.gif │ │ │ ├── gif2.gif │ │ │ ├── gif3.gif │ │ │ └── gif4.gif │ │ ├── ff-logo.png │ │ ├── ffde-logo.png │ │ ├── ffde-logo.svg │ │ ├── foxtail.svg │ │ ├── logo-white.svg │ │ ├── logo.svg │ │ ├── play.svg │ │ ├── terms │ │ │ ├── area.svg │ │ │ ├── cell.svg │ │ │ ├── column.svg │ │ │ ├── grid.svg │ │ │ ├── gutter.svg │ │ │ ├── line.svg │ │ │ ├── numbers.svg │ │ │ ├── row.svg │ │ │ └── track.svg │ │ └── wavydiv.svg │ └── layout │ │ ├── BottomNav.js │ │ ├── CTA.js │ │ ├── DevHomework.js │ │ ├── Footer.js │ │ ├── Header.js │ │ ├── Hero.js │ │ └── Side.js ├── data │ ├── css-grid.js │ └── debugger.js ├── layouts │ ├── img │ │ ├── favicon.ico │ │ └── favicon.png │ └── index.js ├── pages │ ├── 02-first-grid.js │ ├── 03-firefox-devtools.js │ ├── 04-fr-unit.js │ ├── 05-mixing-units.js │ ├── 06-position-items.js │ ├── 07-basic-layout.js │ ├── 08-template-areas.js │ ├── 09-named-lines.js │ ├── 10-learn-more.js │ ├── 404.js │ ├── css-grid │ │ ├── 02-first-grid.js │ │ ├── 03-firefox-devtools.js │ │ ├── 04-fr-unit.js │ │ ├── 05-mixing-units.js │ │ ├── 06-position-items.js │ │ ├── 07-basic-layout.js │ │ ├── 08-template-areas.js │ │ ├── 09-named-lines.js │ │ ├── 10-learn-more.js │ │ ├── components │ │ │ ├── _Main.js │ │ │ └── img │ │ │ │ ├── fb.png │ │ │ │ ├── twitter.gif │ │ │ │ └── twitter.png │ │ └── index.js │ ├── debugger │ │ ├── 02-check-variable-values.js │ │ ├── 03-the-call-stack.js │ │ ├── 04-conditional-breakpoints.js │ │ ├── 05-learn-more.js │ │ ├── components │ │ │ ├── _Main.js │ │ │ └── img │ │ │ │ └── fb.png │ │ ├── img │ │ │ ├── p1 │ │ │ │ ├── overview.png │ │ │ │ └── tools.png │ │ │ ├── p2 │ │ │ │ ├── add.gif │ │ │ │ ├── hover.png │ │ │ │ ├── scopes.png │ │ │ │ ├── title.png │ │ │ │ ├── toolbar.png │ │ │ │ └── watch.gif │ │ │ ├── p3 │ │ │ │ ├── callstack.png │ │ │ │ ├── index.png │ │ │ │ ├── search.gif │ │ │ │ └── stepin.png │ │ │ └── p4 │ │ │ │ └── createbp.gif │ │ └── index.js │ ├── img │ │ ├── dino.gif │ │ └── ffdelogo.png │ └── index.js └── styles │ ├── _config.scss │ ├── _examples.scss │ ├── _shame.scss │ ├── base │ ├── _global.scss │ ├── _grid.scss │ ├── _reset.scss │ └── _type.scss │ ├── components │ ├── _404.scss │ ├── _buttons.scss │ ├── _code-block.scss │ ├── _download-button.scss │ ├── _download-white.scss │ ├── _hero.scss │ ├── _img-caption.scss │ ├── _term.scss │ ├── _tutorial-list.scss │ └── _video.scss │ ├── examples │ ├── 02.module.scss │ └── 04.scss │ ├── layout │ ├── _bottom-nav.scss │ ├── _cta.scss │ ├── _footer.scss │ ├── _header.scss │ ├── _homework.scss │ ├── _layout.scss │ └── _side.scss │ ├── style.scss │ └── util │ ├── _util.scss │ ├── _whitespace.scss │ ├── functions │ ├── _convert.scss │ ├── _math.scss │ └── _spacing.scss │ └── mixins │ ├── _buttons.scss │ ├── _clearfix.scss │ ├── _type.scss │ └── _z-index.scss └── static └── favicon.ico /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "airbnb", 4 | "globals": { 5 | "document": true, 6 | "window": true, 7 | "process": true 8 | }, 9 | "rules": { 10 | "import/extensions": 0, 11 | "import/no-extraneous-dependencies": 0, 12 | "import/no-unresolved": 0, 13 | "react/jsx-filename-extension": 0, 14 | "linebreak-style": 0, 15 | "react/react-in-jsx-scope": 0, 16 | "react/prop-types": [ 17 | "error", 18 | { 19 | "ignore": [ 20 | "children" 21 | ] 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project dependencies 2 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 3 | node_modules 4 | .cache/ 5 | # Build directory 6 | public/ 7 | .DS_Store 8 | yarn-error.log 9 | yarn.lock 10 | haters 11 | old 12 | -------------------------------------------------------------------------------- /.scss-lint.yml: -------------------------------------------------------------------------------- 1 | 2 | severity: warning 3 | 4 | linters: 5 | 6 | BemDepth: 7 | enabled: true 8 | max_elements: 1 9 | 10 | BorderZero: 11 | enabled: true 12 | convention: zero 13 | 14 | BemDepth: 15 | enabled: true 16 | 17 | Comment: 18 | enabled: false 19 | 20 | DeclarationOrder: 21 | enabled: true 22 | 23 | ExtendDirective: 24 | enabled: true 25 | 26 | ElsePlacement: 27 | enabled: false 28 | 29 | IdSelector: 30 | enabled: true 31 | 32 | LeadingZero: 33 | enabled: false 34 | 35 | MergeableSelector: 36 | enabled: false 37 | force_nesting: false 38 | 39 | NameFormat: 40 | enabled: true 41 | 42 | NestingDepth: 43 | enabled: true 44 | max_depth: 2 45 | 46 | PrivateNamingConvention: 47 | enabled: true 48 | prefix: _ 49 | 50 | PropertySortOrder: 51 | enabled: false 52 | 53 | PropertyUnits: 54 | enabled: false 55 | 56 | QualifyingElement: 57 | enabled: false 58 | 59 | SelectorFormat: 60 | enabled: true 61 | convention: hyphenated_BEM 62 | class_convention: ^(?!js-).* 63 | class_convention_explanation: should not be written in the form js-* 64 | 65 | SingleLinePerProperty: 66 | enabled: true 67 | allow_single_line_rule_sets: false 68 | 69 | SpaceAfterVariableName: 70 | enabled: false 71 | 72 | StringQuotes: 73 | enabled: true 74 | style: single_quotes 75 | 76 | TrailingZero: 77 | enabled: true 78 | 79 | UrlFormat: 80 | enabled: false 81 | -------------------------------------------------------------------------------- /.vscode/cSpell.json: -------------------------------------------------------------------------------- 1 | // cSpell Settings 2 | { 3 | // Version of the setting file. Always 0.1 4 | "version": "0.1", 5 | // language - current active spelling language 6 | "language": "en", 7 | // words - list of words to be always considered correct 8 | "words": [ 9 | "Codepen" 10 | ], 11 | // flagWords - list of words to be always considered incorrect 12 | // This is useful for offensive words and common spelling errors. 13 | // For example "hte" should be "the" 14 | "flagWords": [] 15 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mozilla Playground: CSS Grid Layout 2 | A Website to learn how to build with CSS Grid Layout and Firefox DevTools 3 | 4 | [View the website](https://mozilladevelopers.github.io/playground) 5 | 6 | This website was built with Gatsby. 7 | 8 | ### Develop 9 | 10 | To run this locally, install dependencies with NPM and then: 11 | 12 | ``` 13 | npm run dev 14 | ``` 15 | 16 | ### Build 17 | 18 | ``` 19 | npm run build 20 | ``` 21 | 22 | ### Deploy (github pages) 23 | 24 | ``` 25 | npm run deploy 26 | ``` 27 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | const autoprefixer = require('autoprefixer'); 2 | 3 | module.exports = { 4 | siteMetadata: { 5 | title: 'Mozilla Playground', 6 | }, 7 | plugins: [ 8 | 'gatsby-plugin-react-helmet', 9 | { 10 | resolve: 'gatsby-plugin-postcss-sass', 11 | options: { 12 | postCssPlugins: [autoprefixer()], 13 | }, 14 | }, 15 | { 16 | resolve: 'gatsby-plugin-google-analytics', 17 | options: { 18 | trackingId: 'UA-35433268-86', 19 | }, 20 | }, 21 | ], 22 | pathPrefix: '/playground', 23 | }; 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mozilla-playground", 3 | "description": "Learn about CSS Grid Layout", 4 | "version": "1.0.0", 5 | "author": "Dan Brown ", 6 | "dependencies": { 7 | "autoprefixer": "^7.2.5", 8 | "babel-eslint": "^8.2.1", 9 | "babel-plugin-module-resolver": "^3.0.0", 10 | "babel-plugin-wrap-in-js": "^1.1.0", 11 | "classnames": "^2.2.5", 12 | "eslint": "^4.16.0", 13 | "eslint-config-airbnb": "^16.1.0", 14 | "eslint-plugin-import": "^2.6.1", 15 | "eslint-plugin-jsx-a11y": "^6.0.3", 16 | "eslint-plugin-react": "^7.6.0", 17 | "gatsby": "^1.9.169", 18 | "gatsby-link": "^1.6.34", 19 | "gatsby-plugin-google-analytics": "^1.0.16", 20 | "gatsby-plugin-postcss-sass": "^1.0.16", 21 | "gatsby-plugin-react-helmet": "^2.0.4", 22 | "gh-pages": "^1.1.0", 23 | "mappy-breakpoints": "^0.2.3", 24 | "prop-types": "^15.6.0", 25 | "react-bodymovin": "^1.1.1", 26 | "react-helmet": "^5.2.0", 27 | "react-modal-video": "^1.0.7", 28 | "susy": "^3.0.2", 29 | "typeface-antonio": "^1.0.0" 30 | }, 31 | "keywords": [ 32 | "mozilla" 33 | ], 34 | "license": "MIT", 35 | "main": "n/a", 36 | "scripts": { 37 | "build": "gatsby build", 38 | "dev": "gatsby develop", 39 | "format": "prettier-eslint \"src/**/*.js\"", 40 | "deploy": "gatsby build --prefix-paths && gh-pages -d public", 41 | "gh-pages": "gh-pages -d public", 42 | "test": "echo \"Error: no test specified\" && exit 1" 43 | }, 44 | "devDependencies": { 45 | "prettier": "^1.10.2", 46 | "prettier-eslint-cli": "^4.7.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/components/CodeBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const CodeBlock = ({ children }) => ( 5 |
 6 |     {children}
 7 |   
8 | ); 9 | 10 | CodeBlock.propTypes = { 11 | children: PropTypes.node.isRequired, 12 | }; 13 | 14 | export default CodeBlock; 15 | -------------------------------------------------------------------------------- /src/components/CodepenLink.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const style = { 5 | display: 'block', 6 | fontSize: '14px', 7 | marginTop: '1rem', 8 | }; 9 | 10 | const linkStyle = { 11 | color: 'black', 12 | textDecoration: 'underline', 13 | }; 14 | 15 | const CodepenLink = props => ( 16 | 17 | 18 | View on Codepen 19 | 20 | 21 | ); 22 | 23 | CodepenLink.propTypes = { 24 | link: PropTypes.string.isRequired, 25 | }; 26 | 27 | export default CodepenLink; 28 | 29 | -------------------------------------------------------------------------------- /src/components/DownloadButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import download from '../components/img/download.svg'; 3 | 4 | const DownloadButton = ({ children, className, float }) => ( 5 | 9 | ); 10 | 11 | export default DownloadButton; 12 | -------------------------------------------------------------------------------- /src/components/DownloadLink.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const trackClick = () => { 5 | if (typeof ga === 'function') { 6 | ga('send', 'event', { 7 | eventCategory: 'Outbound Link', 8 | eventAction: 'click', 9 | eventLabel: 'Download DevEdition', 10 | }); 11 | } 12 | }; 13 | 14 | const DownloadLink = props => ( 15 | trackClick()} 20 | > 21 | {props.children} 22 | 23 | ); 24 | 25 | DownloadLink.propTypes = { 26 | content: PropTypes.string.isRequired, 27 | }; 28 | 29 | export default DownloadLink; 30 | -------------------------------------------------------------------------------- /src/components/DownloadWhite.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | // images 4 | import arrow from './img/download-blue.svg';; 5 | 6 | const DownloadWhite = () => ( 7 | 11 | ); 12 | 13 | export default DownloadWhite; 14 | -------------------------------------------------------------------------------- /src/components/ImgCaption.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const ImgCaption = props => ( 5 |
6 | {props.alt} 7 | {props.caption} 8 |
9 | ); 10 | 11 | ImgCaption.propTypes = { 12 | src: PropTypes.string.isRequired, 13 | alt: PropTypes.string.isRequired, 14 | caption: PropTypes.string.isRequired, 15 | dark: PropTypes.bool, 16 | }; 17 | 18 | ImgCaption.defaultProps = { 19 | dark: false, 20 | }; 21 | 22 | export default ImgCaption; 23 | -------------------------------------------------------------------------------- /src/components/Redirect.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import PropTypes from 'prop-types'; 4 | import Link from 'gatsby-link'; 5 | 6 | class Redirect extends React.Component { 7 | componentDidMount() { 8 | window.location.href = `/playground${this.props.url}`; 9 | } 10 | 11 | render() { 12 | return ( 13 |
14 | 15 | 16 | 17 | Page Redirect 18 | 19 |
20 |

21 | This page has moved and you will be redirected. If you are not redirected 22 | automatically, follow this link to example. 23 |

24 |
25 |
26 | ); 27 | } 28 | } 29 | 30 | Redirect.propTypes = { 31 | url: PropTypes.string.isRequired, 32 | }; 33 | 34 | export default Redirect; 35 | -------------------------------------------------------------------------------- /src/components/Term.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Term = props => ( 4 |
5 | {props.name} 6 |
7 | {props.name} 8 |

{props.children}

9 |
10 |
11 | ); 12 | 13 | export default Term; 14 | 15 | -------------------------------------------------------------------------------- /src/components/TutorialItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Link from 'gatsby-link'; 4 | 5 | import arrowRight from './img/arrow-right2.svg'; 6 | 7 | const TutorialItem = props => ( 8 |
9 | 10 | 11 | 12 |

{props.title}

13 |

{props.desc}

14 | 15 | 18 | 19 |
20 | ); 21 | 22 | export default TutorialItem; 23 | -------------------------------------------------------------------------------- /src/components/TutorialList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | // components 4 | import TutorialItem from '../components/TutorialItem'; 5 | 6 | // photos 7 | import cssgridPhoto from '../components/img/cssgrid.png'; 8 | import debuggerPhoto from '../components/img/debugger.png'; 9 | 10 | const TutorialList = () => ( 11 |
12 |
13 | 19 | 25 |
26 |
27 | ); 28 | 29 | export default TutorialList; 30 | -------------------------------------------------------------------------------- /src/components/VideoPlayer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | // components 5 | import ModalVideo from 'react-modal-video'; 6 | 7 | // CSS 8 | import '../../node_modules/react-modal-video/scss/modal-video.scss'; 9 | 10 | // images 11 | 12 | import play from './img/play.svg'; 13 | 14 | class VideoPlayer extends React.Component { 15 | constructor() { 16 | super(); 17 | this.state = { 18 | isOpen: false, 19 | }; 20 | } 21 | 22 | openModal = () => { 23 | this.setState({ 24 | isOpen: true, 25 | }); 26 | } 27 | 28 | render() { 29 | return ( 30 |
31 | 32 | this.setState({ isOpen: false })} 37 | /> 38 | 39 | 40 | Launch Video Player 41 | play icon 42 | 43 | 44 |
45 | ); 46 | } 47 | } 48 | 49 | VideoPlayer.propTypes = { 50 | videoId: PropTypes.string.isRequired, 51 | }; 52 | 53 | export default VideoPlayer; 54 | -------------------------------------------------------------------------------- /src/components/img/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 2 Copy 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/components/img/arrow-left2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Page 1 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/components/img/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Page 1 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/components/img/arrow-right2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 2 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/components/img/cssgrid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/components/img/cssgrid.png -------------------------------------------------------------------------------- /src/components/img/current.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 2 Copy 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/components/img/debugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/components/img/debugger.png -------------------------------------------------------------------------------- /src/components/img/done.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Page 1 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/components/img/download-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/components/img/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/components/img/dtgif/gif1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/components/img/dtgif/gif1.gif -------------------------------------------------------------------------------- /src/components/img/dtgif/gif2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/components/img/dtgif/gif2.gif -------------------------------------------------------------------------------- /src/components/img/dtgif/gif3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/components/img/dtgif/gif3.gif -------------------------------------------------------------------------------- /src/components/img/dtgif/gif4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/components/img/dtgif/gif4.gif -------------------------------------------------------------------------------- /src/components/img/ff-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/components/img/ff-logo.png -------------------------------------------------------------------------------- /src/components/img/ffde-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/components/img/ffde-logo.png -------------------------------------------------------------------------------- /src/components/img/logo-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/components/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Logo 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/components/img/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /src/components/img/terms/area.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Grid Area 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/components/img/terms/cell.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Grid Cell 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/components/img/terms/column.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Grid Column 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/components/img/terms/grid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | numbers 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 1 11 | 12 | 13 | 1 14 | 15 | 16 | 2 17 | 18 | 19 | 3 20 | 21 | 22 | 4 23 | 24 | 25 | 2 26 | 27 | 28 | 3 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/components/img/terms/gutter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Grid Gutter 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/components/img/terms/line.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Grid Line 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/components/img/terms/numbers.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Gridline Numbers 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 1 14 | 15 | 16 | 1 17 | 18 | 19 | 2 20 | 21 | 22 | 3 23 | 24 | 25 | 4 26 | 27 | 28 | 2 29 | 30 | 31 | 3 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/components/img/terms/row.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Grid Row 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/components/img/terms/track.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Grid Column 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/components/img/wavydiv.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/components/layout/BottomNav.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Link from 'gatsby-link'; 4 | 5 | 6 | // Generate Previous Page Link (if this isn't the first page); 7 | const PreviousLink = ({ currentPageNum, pageData }) => { 8 | if (currentPageNum !== 1) { 9 | const previousPage = pageData.find(page => page.order === currentPageNum - 1); 10 | const prevPageName = previousPage.title; 11 | const prevPageLink = previousPage.link; 12 | return ( 13 |
14 | 15 |
16 | Previous 17 | {prevPageName} 18 |
19 | 20 |
21 | ); 22 | } 23 | return
; 24 | }; 25 | 26 | // Generate Next Page Link (if this isn't the first page); 27 | const NextLink = ({ currentPageNum, pageData }) => { 28 | if (currentPageNum !== pageData.length) { 29 | const NextPage = pageData.find(page => page.order === currentPageNum + 1); 30 | const nextPageName = NextPage.title; 31 | const nextPageLink = NextPage.link; 32 | return ( 33 |
34 | 35 |
36 | Next 37 | {nextPageName} 38 |
39 | 40 |
41 | ); 42 | } 43 | return
; 44 | }; 45 | 46 | const BottomNav = ({ currentPageNum, pageData }) => ( 47 |
48 |
49 | 50 | 51 |
52 |
53 | ); 54 | 55 | BottomNav.propTypes = { 56 | currentPageNum: PropTypes.number.isRequired, 57 | }; 58 | 59 | NextLink.propTypes = { 60 | currentPageNum: PropTypes.number.isRequired, 61 | }; 62 | 63 | PreviousLink.propTypes = { 64 | currentPageNum: PropTypes.number.isRequired, 65 | }; 66 | 67 | export default BottomNav; 68 | -------------------------------------------------------------------------------- /src/components/layout/CTA.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | // Components 4 | import DownloadWhite from '../../components/DownloadWhite'; 5 | import DownloadLink from '../../components/DownloadLink'; 6 | 7 | // images 8 | import logo from '../img/ffde-logo.png'; 9 | 10 | const CTA = () => ( 11 |
12 |
13 |
14 |
15 | Firefox Developer Edition Logo 16 |
17 | Get the browser with the best tools for Developers 18 |
19 | 20 | 21 | 22 |
23 |
24 |
25 |
26 | ); 27 | 28 | 29 | export default CTA; 30 | -------------------------------------------------------------------------------- /src/components/layout/DevHomework.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import logo from '../img/ff-logo.png'; 5 | 6 | const DevHomework = props => ( 7 |
8 |
9 | Firefox Logo 10 |

{props.title}

11 |
12 |
{props.children}
13 |
14 |
15 |
16 | ); 17 | 18 | DevHomework.PropTypes = { 19 | title: PropTypes.string.isRequired, 20 | }; 21 | 22 | export default DevHomework; 23 | -------------------------------------------------------------------------------- /src/components/layout/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import logo from '../img/logo-white.svg'; 4 | 5 | export default () => ( 6 | 103 | ); 104 | -------------------------------------------------------------------------------- /src/components/layout/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'gatsby-link'; 3 | 4 | // images 5 | import logo from '../img/logo.svg'; 6 | 7 | // components 8 | import DownloadButton from '../../components/DownloadButton'; 9 | import DownloadLink from '../../components/DownloadLink'; 10 | 11 | const Header = () => ( 12 |
13 |
14 | 15 | logo 16 | 17 | 18 | 19 | 20 | Download Firefox 21 | 22 | 23 |
24 |
25 | ); 26 | 27 | export default Header; 28 | -------------------------------------------------------------------------------- /src/components/layout/Hero.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Link from 'gatsby-link'; 4 | 5 | // images 6 | import arrowLeft from '../img/arrow-left2.svg'; 7 | 8 | const Hero = props => ( 9 |
10 | {props.breadcrumb && ( 11 |
12 | 13 |
14 | arrow left 15 | Playground Home 16 |
17 | 18 |
19 | )} 20 | {props.children} 21 | 22 | 31 | 32 | 33 | 38 | 39 | 40 |
41 | ); 42 | 43 | Hero.propTypes = { 44 | className: PropTypes.string, 45 | breadcrumb: PropTypes.bool, 46 | }; 47 | 48 | Hero.defaultProps = { 49 | background: '#FFF', 50 | className: undefined, 51 | breadcrumb: undefined, 52 | }; 53 | 54 | export default Hero; 55 | -------------------------------------------------------------------------------- /src/components/layout/Side.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classNames from 'classnames'; 4 | 5 | import Link from 'gatsby-link'; 6 | 7 | // images 8 | import done from '../img/done.svg'; 9 | import current from '../img/current.svg'; 10 | 11 | // individual side-nav items 12 | const SideItem = props => ( 13 | 14 |
21 |
22 |
23 |
24 | {props.currentPageNum === props.order ? current : ''} 25 | {props.order < props.currentPageNum ? done : ''} 26 |
27 |
28 | {props.title} 29 |
30 |
props.order && props.order !== props.pageData.length 33 | ? 'side-nav__line is-active' 34 | : 'side-nav__line' 35 | } 36 | /> 37 |
38 | 39 | ); 40 | 41 | // The entire sidebar 42 | const Side = (props) => { 43 | const pages = props.pageData.map(page => ( 44 | 52 | )); 53 | return
{pages}
; 54 | }; 55 | 56 | SideItem.propTypes = { 57 | currentPageNum: PropTypes.number.isRequired, 58 | order: PropTypes.number.isRequired, 59 | title: PropTypes.string.isRequired, 60 | link: PropTypes.string.isRequired, 61 | }; 62 | 63 | Side.propTypes = { 64 | currentPageNum: PropTypes.number.isRequired, 65 | }; 66 | 67 | SideItem.defaultProps = { 68 | active: false, 69 | }; 70 | 71 | export default Side; 72 | -------------------------------------------------------------------------------- /src/data/css-grid.js: -------------------------------------------------------------------------------- 1 | const pageData = [ 2 | { 3 | order: 1, 4 | title: 'Terminology', 5 | link: '/css-grid', 6 | }, 7 | { 8 | order: 2, 9 | title: 'Your First Grid', 10 | link: '/css-grid/02-first-grid/', 11 | }, 12 | { 13 | order: 3, 14 | title: 'Firefox DevTools', 15 | link: '/css-grid/03-firefox-devtools', 16 | }, 17 | { 18 | order: 4, 19 | title: 'The fr Unit', 20 | link: '/css-grid/04-fr-unit', 21 | }, 22 | { 23 | order: 5, 24 | title: 'Mixing Units', 25 | link: '/css-grid/05-mixing-units', 26 | }, 27 | { 28 | order: 6, 29 | title: 'Position Items', 30 | link: '/css-grid/06-position-items', 31 | }, 32 | { 33 | order: 7, 34 | title: 'Basic Layout', 35 | link: '/css-grid/07-basic-layout', 36 | }, 37 | { 38 | order: 8, 39 | title: 'Template Areas', 40 | link: '/css-grid/08-template-areas', 41 | }, 42 | { 43 | order: 9, 44 | title: 'Named Lines', 45 | link: '/css-grid/09-named-lines', 46 | }, 47 | { 48 | order: 10, 49 | title: 'Learn More', 50 | link: '/css-grid/10-learn-more', 51 | }, 52 | ]; 53 | 54 | export default pageData; 55 | -------------------------------------------------------------------------------- /src/data/debugger.js: -------------------------------------------------------------------------------- 1 | const pageData = [ 2 | { 3 | order: 1, 4 | title: 'Introduction', 5 | link: '/debugger', 6 | }, 7 | { 8 | order: 2, 9 | title: 'Check Values', 10 | link: '/debugger/02-check-variable-values/', 11 | }, 12 | { 13 | order: 3, 14 | title: 'The Call Stack', 15 | link: '/debugger/03-the-call-stack', 16 | }, 17 | { 18 | order: 4, 19 | title: 'Conditional Breakpoints', 20 | link: '/debugger/04-conditional-breakpoints', 21 | }, 22 | { 23 | order: 5, 24 | title: 'Learn More', 25 | link: '/debugger/05-learn-more', 26 | } 27 | ]; 28 | 29 | export default pageData; 30 | -------------------------------------------------------------------------------- /src/layouts/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/layouts/img/favicon.ico -------------------------------------------------------------------------------- /src/layouts/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/layouts/img/favicon.png -------------------------------------------------------------------------------- /src/layouts/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import 'typeface-antonio'; 4 | 5 | // images 6 | import faviconico from './img/favicon.ico'; 7 | import faviconpng from './img/favicon.png'; 8 | import logo from '../pages/img/ffdelogo.png'; 9 | 10 | // styles 11 | import '../styles/style.scss'; 12 | 13 | // Components 14 | import Header from '../components/layout/Header'; 15 | import Footer from '../components/layout/Footer'; 16 | import CTA from '../components/layout/CTA'; 17 | 18 | const Layout = ({ children }) => ( 19 |
20 | 21 | 22 | 23 | {'Firefox DevTools PlayGround | Mozilla'} 24 | 28 | 29 | 30 | 34 | 35 | 36 | 37 | 38 | 39 | 43 | 44 | 45 | 46 |
47 | 48 | {children()} 49 | 50 | 51 |
52 |
53 | ); 54 | 55 | export default Layout; 56 | -------------------------------------------------------------------------------- /src/pages/02-first-grid.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Redirect from '../components/Redirect'; 3 | 4 | export default () => { 5 | return ( 6 | 7 | ); 8 | }; 9 | -------------------------------------------------------------------------------- /src/pages/03-firefox-devtools.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Redirect from '../components/Redirect'; 3 | 4 | export default () => { 5 | return ( 6 | 7 | ); 8 | }; 9 | -------------------------------------------------------------------------------- /src/pages/04-fr-unit.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Redirect from '../components/Redirect'; 3 | 4 | export default () => { 5 | return ( 6 | 7 | ); 8 | }; 9 | -------------------------------------------------------------------------------- /src/pages/05-mixing-units.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Redirect from '../components/Redirect'; 3 | 4 | export default () => { 5 | return ( 6 | 7 | ); 8 | }; 9 | -------------------------------------------------------------------------------- /src/pages/06-position-items.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Redirect from '../components/Redirect'; 3 | 4 | export default () => { 5 | return ( 6 | 7 | ); 8 | }; 9 | -------------------------------------------------------------------------------- /src/pages/07-basic-layout.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Redirect from '../components/Redirect'; 3 | 4 | export default () => { 5 | return ( 6 | 7 | ); 8 | }; 9 | -------------------------------------------------------------------------------- /src/pages/08-template-areas.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Redirect from '../components/Redirect'; 3 | 4 | export default () => { 5 | return ( 6 | 7 | ); 8 | }; 9 | -------------------------------------------------------------------------------- /src/pages/09-named-lines.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Redirect from '../components/Redirect'; 3 | 4 | export default () => { 5 | return ( 6 | 7 | ); 8 | }; 9 | -------------------------------------------------------------------------------- /src/pages/10-learn-more.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Redirect from '../components/Redirect'; 3 | 4 | export default () => { 5 | return ( 6 | 7 | ); 8 | }; 9 | -------------------------------------------------------------------------------- /src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | // components 4 | import Hero from '../components/layout/Hero'; 5 | import TutorialList from '../components/TutorialList'; 6 | 7 | // images 8 | import dino from './img/dino.gif'; 9 | 10 | const NotFoundPage = () => ( 11 |
12 | 13 |
14 |
15 |
16 | lost dino 17 |
18 |
19 |

This page has moved!

20 |

21 | The page you are looking for doesn't exist, or has moved. Scroll down to find the 22 | current tutorials & playgrounds that are available. 23 |

24 |
25 |
26 |
27 |
28 | 29 |
30 | ); 31 | 32 | export default NotFoundPage; 33 | -------------------------------------------------------------------------------- /src/pages/css-grid/02-first-grid.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | import CodeBlock from '../../components/CodeBlock'; 4 | import DevHomework from '../../components/layout/DevHomework'; 5 | import CodepenLink from '../../components/CodepenLink'; 6 | import DownloadLink from '../../components/DownloadLink'; 7 | import VideoPlayer from '../../components/VideoPlayer'; 8 | 9 | 10 | const Tutorial = () => ( 11 |
12 | 13 | 14 |

Your First Grid

15 | 16 |

Create a grid

17 |

18 | The first thing we want to do is create a grid container. We can do this 19 | by declaring display: grid on the container element. In this 20 | example we are using a div with the class of container. 21 |

22 | 23 |

Define rows and columns

24 |

25 | There are several ways to define rows and columns. For our first grid, we 26 | will use properties grid-template-columns and{" "} 27 | grid-template-rows. These properties allow us to define the 28 | size of the rows and columns for our grid. To create two fixed-height rows 29 | of 150px and three fixed-width columns of 150px, simply write: 30 |

31 | 32 | 33 | {` 34 | grid-template-columns: 150px 150px 150px; 35 | grid-template-rows: 150px 150px; 36 | `} 37 | 38 | 39 |

To add a fourth column that is 70px wide, write:

40 | 41 | 42 | {` 43 | grid-template-columns: 150px 150px 150px 70px; 44 | `} 45 | 46 | 47 |

...and so on to add more columns.

48 | 49 |

Add a gutter

50 | 51 |

52 | Adding a gutter to your grid is amazingly easy with CSS Grid Layout. 53 | Simply add: 54 |

55 | 56 | 57 | {` 58 | grid-gap: 1rem; 59 | `} 60 | 61 | 62 |

63 | That simple line gives you an equal-sized gutter between all rows and 64 | columns. To define the gutter size for columns and rows individually, you 65 | can use the grid-column-gap and grid-row-gap{" "} 66 | properties instead. 67 |

68 | 69 |

Now let's put that all together. Here is our HTML:

70 | 71 | 72 | {` 73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | `} 82 |
83 | 84 |

With just a few lines of CSS, we can create a simple grid:

85 | 86 | 87 | {` 88 | .container { 89 | display: grid; 90 | grid-template-columns: 150px 150px 150px; 91 | grid-template-rows: 150px 150px; 92 | grid-gap: 1rem; 93 | } 94 | `} 95 | 96 |

Here is the result:

97 | 98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | 107 | 108 |
109 | ); 110 | 111 | const Homework = () => ( 112 | 113 |

114 | Amazing right? Inspect the grid above with your browser's developer tools. Try changing the 115 | column width, or the row height. Swap out the grid-gap property with the 116 | grid-column-gap and grid-row-gap properties and play around with 117 | different widths and heights. 118 |

119 | 120 |

121 | Having a good set of developer tools when working with CSS Grid Layout is essential. Firefox 122 | has some fantastic features that are specifically built to help you create and design grids. 123 | Intrigued?{' '} 124 | Download Firefox Developer Edition to get 125 | the browser with the best CSS Grid Layout tools. 126 |

127 | 128 |

Click to the next section to learn about Firefox's new CSS Grid Layout panel.

129 |
130 | ); 131 | 132 | export default () =>
} homework={} />; 133 | -------------------------------------------------------------------------------- /src/pages/css-grid/03-firefox-devtools.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | import VideoPlayer from '../../components/VideoPlayer'; 4 | 5 | // images 6 | import gif1 from '../../components/img/dtgif/gif1.gif'; 7 | import gif2 from '../../components/img/dtgif/gif2.gif'; 8 | import gif3 from '../../components/img/dtgif/gif3.gif'; 9 | import gif4 from '../../components/img/dtgif/gif4.gif'; 10 | 11 | const imgStyle = { 12 | width: '100%', 13 | marginBottom: '3rem', 14 | }; 15 | 16 | const Tutorial = () => ( 17 |
18 | 19 |

Firefox DevTools

20 | 21 |

22 | Designers and developers are rapidly falling in love with CSS Grid Layout. 23 | That’s why Mozilla has been working hard on the Firefox Developer Tools 24 | Layout panel, adding powerful upgrades to the CSS Grid Inspector and Box 25 | Model. 26 |

27 | 28 |

CSS Grid Overlay

29 |

30 | The new CSS Layout panel lists all the available CSS Grid containers on 31 | the page and includes an overlay to help you visualize the grid itself. 32 | You can customize the information displayed on the overlay, including grid 33 | line numbers and dimensions. 34 |

35 | enable grid overlay on a grid 36 | 37 |

Interactive grid

38 |

39 | There is a new interactive grid outline in the sidebar. Mouse over the 40 | outline to highlight parts of the grid on the pages and display size, 41 | area, and position information. 42 |

43 | grid outline 44 | 45 |

Display grid area

46 |

47 | The new “Display grid areas” setting shows the bounding areas and the 48 | associated area name in every cell. We'll learn more about how to set a 49 | grid area name in a bit. 50 |

51 | grid area 52 | 53 |

Visualize transformations

54 |

55 | The Grid Inspector is capable of visualizing transformations applied to 56 | the grid container. This lets you accurately see where the grid lines are 57 | on the page for any grids that are translated, skewed, rotated, or scaled. 58 |

59 | grid transition 60 | 61 |

62 | These features and improvements are currently available in Firefox Nightly 63 | and Firefox Developer edition. It is recommended that you download and 64 | install one of these browsers before continuing. These features are only 65 | available in Firefox and will help you as you learn about the ins and outs 66 | of CSS Grid Layout. 67 |

68 | 69 | 74 | 77 | 78 |
79 | ); 80 | 81 | export default () =>
} />; 82 | -------------------------------------------------------------------------------- /src/pages/css-grid/04-fr-unit.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | import CodeBlock from '../../components/CodeBlock'; 4 | import DevHomework from '../../components/layout/DevHomework'; 5 | import CodepenLink from '../../components/CodepenLink'; 6 | import VideoPlayer from '../../components/VideoPlayer'; 7 | 8 | const Tutorial = () => ( 9 |
10 | 11 | 12 |

The fr Unit

13 | 14 |

15 | In our first grid, we created columns with a fixed px width. That's great, 16 | but it isn't very flexible. Thankfully, CSS Grid Layout introduces a new 17 | unit of length called fr, which is short for “fraction”. MDN defines the 18 | fr unit as a unit which represents a fraction of the available space in 19 | the grid container. If we want to rewrite our previous grid to have three 20 | equal-width columns, we could change our CSS to use the fr unit: 21 |

22 | 23 | 24 | {` 25 | .container { 26 | display: grid; 27 | width: 800px; 28 | grid-template-columns: 1fr 1fr 1fr; 29 | grid-template-rows: 150px 150px; 30 | grid-gap: 1rem; 31 | } 32 | `} 33 | 34 | 35 |

The repeat() notation

36 | 37 |

38 | Handy tip: If you find yourself repeating length units, use the CSS 39 | repeat() function. Rewrite the above code like so: 40 |

41 | 42 | 43 | {` 44 | .container { 45 | display: grid; 46 | width: 800px; 47 | grid-template-columns: repeat(3, 1fr); 48 | grid-template-rows: repeat(2, 150px); 49 | grid-gap: 1rem; 50 | } 51 | `} 52 | 53 | 54 |

Here is the result:

55 | 56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | 65 | 66 |
67 | ); 68 | 69 | const Homework = () => ( 70 | 71 |

72 | Inspect the above grid and change the grid-template-columns property on the grid 73 | container to the following: 74 |

75 | 76 | 77 | {` 78 | grid-template-columns: 10px repeat(2, 1fr); 79 | `} 80 | 81 |

82 | What happened? As you can see, you can not only use the repeat() notation for just part of the 83 | track listing, but you can also mix units (in this case, px and fr). 84 |

85 | 86 |

We will learn more about mixing units in the next section.

87 |
88 | ); 89 | 90 | export default () =>
} homework={} />; 91 | -------------------------------------------------------------------------------- /src/pages/css-grid/05-mixing-units.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | import CodeBlock from '../../components/CodeBlock'; 4 | import DevHomework from '../../components/layout/DevHomework'; 5 | import CodepenLink from '../../components/CodepenLink'; 6 | 7 | const Tutorial = () => ( 8 |
9 |

Mixing Units

10 | 11 |

12 | When declaring track sizes, you can use fixed sizes with units such as px and em. You can also 13 | use flexible sizes such as percentages or the fr unit. The real magic of CSS Grid Layout, 14 | however, is the ability to mix these units. The best way to understand is with an example: 15 |

16 | 17 | {` 18 | .container { 19 | width: 100%; 20 | display: grid; 21 | grid-template-columns: 100px 30% 1fr; 22 | grid-template-rows: 200px 100px; 23 | grid-gap: 1rem; 24 | } 25 | 26 | `} 27 | 28 | 29 |

30 | Here, we have declared a grid with three columns. The first column is a fixed width of 100px. 31 | The second column will occupy 30% of the available space, and the third column is 1fr which 32 | means it will take up a fraction of the available space. In this case, it will take up all of 33 | the remaining space (1/1). 34 |

35 | 36 |

Here is our HTML:

37 | 38 | 39 | {` 40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | `} 49 | 50 | 51 |

Here is the result:

52 | 53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | 62 | 63 | 64 |
65 | ); 66 | 67 | const Homework = () => ( 68 | 69 |

70 | Inspect the grid above, and change the grid-template-columns property to the 71 | following: 72 |

73 | 74 | {` 75 | grid-template-columns: 100px 30% 2fr 1fr; 76 | `} 77 | 78 | 79 |

80 | Do you see what happened? Instead of 3 columns, you now have a 3rd column that is{' '} 81 | 2fr and occupies 2/3 of the remaining space, and a 4th column that is{' '} 82 | 1fr and occupies the final 1/3 of the remaining space. Continue to play around in 83 | Firefox DevTools and try different units and combinations. 84 |

85 | 86 |

When you are ready, continue on to learn about how to position items on the grid.

87 |
88 | ); 89 | 90 | export default () =>
} homework={} />; 91 | -------------------------------------------------------------------------------- /src/pages/css-grid/06-position-items.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | import CodeBlock from '../../components/CodeBlock'; 4 | import DevHomework from '../../components/layout/DevHomework'; 5 | import CodepenLink from '../../components/CodepenLink'; 6 | 7 | // images 8 | import numbers from '../../components/img/terms/numbers.svg'; 9 | import grid from '../../components/img/terms/grid.svg'; 10 | 11 | const style = { 12 | width: '100%', 13 | margin: '1.5rem 0', 14 | }; 15 | 16 | // images 17 | 18 | const Tutorial = () => ( 19 |
20 |

Position Items

21 | 22 |

Understanding grid lines

23 | 24 |

25 | Now that we are comfortable creating a grid and defining the row and column sizes, we can move 26 | on to placing items on this grid. There are several ways to place items, but we will start 27 | with a basic example. Consider a simple 3x2 grid: 28 |

29 | 30 | a 3x2 grid 31 | 32 |

Each item within this grid will be placed automatically in the default order.

33 | 34 |

35 | If we wish to have greater control, we can position items on the grid using grid line numbers. 36 | Grid lines are numbered left to right and top to bottom (if you are working in a right-to-left 37 | language, then grid lines are numbered right to left). The above example would be numbered 38 | like so: 39 |

40 | 41 | a 3x2 grid with numbers 42 | 43 |

Position an item

44 | 45 |

Here is the HTML we will be using for this example:

46 | 47 | 48 | {` 49 |
50 |
1
51 |
2
52 |
3
53 |
4
54 |
5
55 |
6
56 |
57 | `} 58 |
59 | 60 |

61 | Say we want to position our first grid item (with a class of item1) to be in the second row 62 | and occupy the second column. This item will need to start at the second row line, and span to 63 | the third row line. It will also need to start at the second column line and span to the third 64 | column line. We could write our CSS like so: 65 |

66 | 67 | 68 | {` 69 | .item1 { 70 | grid-row-start: 2; 71 | grid-row-end: 3; 72 | grid-column-start: 2; 73 | grid-column-end: 3; 74 | } 75 | `} 76 | 77 | 78 |

Shorthand property

79 | 80 |

We can also rewrite this with shorthand properties:

81 | 82 | 83 | {` 84 | .item1 { 85 | grid-row: 2 / 3; 86 | grid-column: 2 / 3; 87 | } 88 | `} 89 | 90 | 91 |

Here is the result:

92 | 93 |
94 |
1
95 |
2
96 |
3
97 |
4
98 |
5
99 |
6
100 |
101 | 102 | 103 | 104 |
105 | ); 106 | 107 | const Homework = () => ( 108 | 109 |

110 | Try changing the grid-row property of item1 to the following: 111 |

112 | 113 | {` 114 | .item1 { 115 | grid-row: 3 / 4; 116 | grid-column: 1 / 3; 117 | } 118 | `} 119 | 120 |

121 | See what happened? The item spanned multiple columns from grid line 1 to 3. It also was placed 122 | between grid row lines 3 and 4 which results in a new row being created. This new row is an 123 | implicit row, and its height is set by the grid-auto-rows property on the parent 124 | grid. You can learn more about default rules for auto-placement on{' '} 125 | 130 | MDN 131 | . 132 |

133 | 134 |

Now let's put this new knowledge to work by creating a basic layout.

135 |
136 | ); 137 | 138 | export default () =>
} homework={} />; 139 | -------------------------------------------------------------------------------- /src/pages/css-grid/07-basic-layout.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | import CodeBlock from '../../components/CodeBlock'; 4 | import DevHomework from '../../components/layout/DevHomework'; 5 | import CodepenLink from '../../components/CodepenLink'; 6 | import DownloadLink from '../../components/DownloadLink'; 7 | 8 | const Tutorial = () => ( 9 |
10 |

Creating a Basic Layout

11 | 12 |

13 | Now that we have learned how to create a grid and position items on that grid, let's create a 14 | basic layout. We won't be introducing any new concepts here. We'll simply be using the 15 | grid-row and grid-column shorthand properties to manually place 16 | items such as a header, footer, and so on. 17 |

18 | 19 |

Here is the HTML:

20 | 21 | 22 | {` 23 |
24 |
header
25 | 26 |
Content-1
27 |
Content-2
28 |
Content-3
29 | 30 |
31 | `} 32 |
33 | 34 |

Here is the CSS:

35 | 36 | 37 | {` 38 | .container { 39 | display: grid; 40 | width: 750px; 41 | height: 600px; 42 | grid-template-columns: 200px 1fr 1fr; 43 | grid-template-rows: 80px 1fr 1fr 100px; 44 | grid-gap: 1rem; 45 | } 46 | 47 | .header { 48 | grid-row: 1 / 2; 49 | grid-column: 1 / 4; 50 | } 51 | 52 | .sidebar { 53 | grid-row: 2 / 4; 54 | grid-column: 1 / 2; 55 | } 56 | 57 | .content-1 { 58 | grid-row: 2 / 3; 59 | grid-column: 2 / 4; 60 | } 61 | 62 | .content-2 { 63 | grid-row: 3 / 4; 64 | grid-column: 2 / 3; 65 | } 66 | 67 | .content-3 { 68 | grid-row: 3 / 4; 69 | grid-column: 3 / 4; 70 | } 71 | 72 | .footer { 73 | grid-row: 4 / 5; 74 | grid-column: 1 / 4; 75 | } 76 | `} 77 | 78 | 79 |

Here is the result:

80 | 81 |
82 |
header
83 |
sidebar
84 |
Content-1
85 |
Content-2
86 |
Content-3
87 |
footer
88 |
89 | 90 | 91 |
92 | ); 93 | 94 | const Homework = () => ( 95 | 96 |

97 | This is the perfect time to test out the 'display line numbers' setting on the Firefox CSS 98 | Grid Layout Panel. Inspect the result above and select the layout panel. Here you can activate 99 | the overlay grid and check the box to 'display line numbers'. Handy right? This tool makes it 100 | very easy to visualize your grid when positioning items. Don't have Firefox?{' '} 101 | 102 | Download Firefox Developer Edition 103 | . 104 |

105 |
106 | ); 107 | 108 | export default () =>
} homework={} />; 109 | -------------------------------------------------------------------------------- /src/pages/css-grid/08-template-areas.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | import CodeBlock from '../../components/CodeBlock'; 4 | import DevHomework from '../../components/layout/DevHomework'; 5 | import CodepenLink from '../../components/CodepenLink'; 6 | import DownloadLink from '../../components/DownloadLink'; 7 | 8 | const Tutorial = () => ( 9 |
10 |

Template Areas

11 |

12 | In our previous example, we learned how to create a basic layout by positioning items with 13 | grid lines. Another method for positioning items is to use named grid areas with the{' '} 14 | grid-template-areas and grid-area properties. The best way to 15 | explain this is with an example. Let's recreate the grid from our previous example with the 16 | grid-template-areas property: 17 |

18 | 19 | {` 20 | .container { 21 | display: grid; 22 | width: 100%; 23 | height: 600px; 24 | grid-template-columns: 200px 1fr 1fr; 25 | grid-template-rows: 80px 1fr 1fr 100px; 26 | grid-gap: 1rem; 27 | grid-template-areas: 28 | "header header header" 29 | "sidebar content-1 content-1" 30 | "sidebar content-2 content-3" 31 | "footer footer footer"; 32 | } 33 | `} 34 | 35 |

36 | Here we have defined three columns and four rows. Instead of placing each individual item, we 37 | can define the entire layout using the grid-template-areas property. We can then 38 | assign those areas to each grid item using the grid-area property. 39 |

40 |

Our HTML:

41 | 42 | {` 43 |
44 |
header
45 | 46 |
Content-1
47 |
Content-2
48 |
Content-3
49 | 50 |
51 | `} 52 |
53 | The rest of our CSS: 54 | 55 | {` 56 | .header { 57 | grid-area: header; 58 | } 59 | 60 | .sidebar { 61 | grid-area: sidebar; 62 | } 63 | 64 | .content-1 { 65 | grid-area: content-1; 66 | } 67 | 68 | .content-2 { 69 | grid-area: content-2; 70 | } 71 | 72 | .content-3 { 73 | grid-area: content-3; 74 | } 75 | 76 | .footer { 77 | grid-area: footer; 78 | } 79 | `} 80 | 81 |

Here is the result:

82 |
83 |
header
84 |
sidebar
85 |
Content-1
86 |
Content-2
87 |
Content-3
88 |
footer
89 |
90 | 91 |
92 | ); 93 | 94 | const Homework = () => ( 95 | 96 |

97 | Did you know that FireFox DevTools can display the area names? Try it out! Inspect the grid 98 | above and open the layout panel. From here you can toggle the overlay grid and the 'Display 99 | Area Names' feature. Don't have Firefox?{' '} 100 | Download Firefox Developer Edition. 101 |

102 |
103 | ); 104 | 105 | export default () =>
} homework={} />; 106 | -------------------------------------------------------------------------------- /src/pages/css-grid/09-named-lines.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | import CodeBlock from '../../components/CodeBlock'; 4 | import DevHomework from '../../components/layout/DevHomework'; 5 | import CodepenLink from '../../components/CodepenLink'; 6 | import DownloadLink from '../../components/DownloadLink'; 7 | 8 | const Tutorial = () => ( 9 |
10 |

Named Lines

11 | 12 |

13 | In a previous example, we learned how to place an item on the grid by providing the {' '} 14 | grid-column and grid-row properties with specific grid lines. We can 15 | also name some or all of those grid lines when defining a grid. This allows us to use those 16 | names instead of grid lines. To name a grid line, simply add the name in square brackets: 17 |

18 | 19 |

To name a grid line, we simply provide the name in square brackets:

20 | 21 | 22 | {` 23 | .container { 24 | display: grid; 25 | width: 100%; 26 | height: 600px; 27 | grid-gap: 1rem; 28 | grid-template-columns: 29 | [main-start sidebar-start] 200px 30 | [sidebar-end content-start] 1fr 31 | [column3-start] 1fr 32 | [content-end main-end]; 33 | grid-template-rows: 34 | [row1-start] 80px 35 | [row2-start] 1fr 36 | [row3-start] 1fr 37 | [row4-start] 100px 38 | [row4-end]; 39 | } 40 | `} 41 | 42 | 43 |

44 | Now that we have line names, we can use those names when placing items. Let's recreate our 45 | basic layout using named lines, instead of line numbers: 46 |

47 | 48 | 49 | {` 50 | .header { 51 | grid-column: main-start / main-end; 52 | grid-row: row1-start / row2-start; 53 | } 54 | 55 | .sidebar { 56 | grid-column: sidebar-start / sidebar-end; 57 | grid-row: row2-start / row4-start; 58 | } 59 | 60 | .content-1 { 61 | grid-column: content-start / content-end; 62 | grid-row: row2-start / row3-start; 63 | } 64 | 65 | .content-2 { 66 | grid-column: content-start / column3-start; 67 | grid-row: row3-start / row4-start; 68 | } 69 | 70 | .content-3 { 71 | grid-column: column3-start / content-end; 72 | grid-row: row3-start / row4-start; 73 | } 74 | 75 | .footer { 76 | grid-column: main-start / main-end; 77 | grid-row: row4-start / row4-end; 78 | } 79 | `} 80 | 81 | 82 |

Here is our HTML:

83 | 84 | 85 | {` 86 |
87 |
header
88 | 89 |
Content-1
90 |
Content-2
91 |
Content-3
92 | 93 |
94 | `} 95 |
96 | 97 |

Here is the result:

98 | 99 |
100 |
header
101 |
sidebar
102 |
Content-1
103 |
Content-2
104 |
Content-3
105 |
footer
106 |
107 | 108 |
109 | ); 110 | 111 | const Homework = () => ( 112 | 113 |

114 | Did you know you can customize the color of the grid overlay in Firefox DevTools? The above 115 | example is on a white background, and the default purple may not be the best color to use. 116 | When selecting an overlay grid to display, you will see a circle next to the grid name that 117 | indicates the color of the overlay. Click on that circle, and you can customize the color to 118 | whatever you'd like. Try a different color, such as red. Don't have Firefox?{' '} 119 | 120 | Download Firefox Developer Edition 121 | . 122 |

123 |
124 | ); 125 | 126 | export default () =>
} homework={} />; 127 | -------------------------------------------------------------------------------- /src/pages/css-grid/10-learn-more.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | import DevHomework from '../../components/layout/DevHomework'; 4 | import DownloadButton from '../../components/DownloadButton'; 5 | import DownloadLink from '../../components/DownloadLink'; 6 | 7 | const Tutorial = () => ( 8 |
9 |

Learn More

10 | 11 |

12 | Hopefully, this short tutorial series has provided you with the knowledge you need to start 13 | experimenting and building with CSS Grid Layout. CSS Grid Layout is powerful, and we only 14 | scratched the surface of what is possible. 15 |

16 | 17 |

18 | If you are ready to dive deeper and learn more, here are a ton of great resources to explore. 19 |

20 | 21 |

Jen Simmons

22 |

23 | Jen Simmons is a Designer Advocate at Mozilla. She is also a developer, writer, and speaker 24 | and is a member of the CSS Working Group. 25 |

26 | 34 | 35 |

Rachel Andrew

36 |

37 | Rachel Andrew is a developer, speaker, and author. She is a member of the CSS Working Group 38 | and develops resources for learning about CSS Grid Layout. 39 |

40 | 48 | 49 |

MDN

50 |

MDN has comprehensive tutorials and documentation for every feature of CSS Grid Layout

51 |
    52 |
  • 53 | MDN 54 |
  • 55 |
56 |
57 | ); 58 | 59 | const Homework = () => ( 60 | 61 |

62 | Ready to be on the cutting edge? Firefox is constantly adding new features and tools that help 63 | developers build for the open web. 64 |

65 | Firefox Developer Edition 66 |

Build, test, scale and more with the only browser built just for developers.

67 |
68 | 69 | Get Firefox Developer Edition 70 | 71 |
72 | 73 |

Firefox Nightly

74 |

75 | Get a sneak peek at the future. Firefox Nightly has daily updates and provides access to new 76 | features and tools before they are released. 77 |

78 | 83 | Get Firefox Nightly 84 | 85 |
86 | ); 87 | 88 | export default () =>
} homework={} />; 89 | -------------------------------------------------------------------------------- /src/pages/css-grid/components/_Main.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import PropTypes from 'prop-types'; 4 | 5 | // data 6 | import pageData from '../../../data/css-grid'; 7 | 8 | // images 9 | import facebook from './img/fb.png'; 10 | 11 | // Components 12 | import Side from '../../../components/layout/Side'; 13 | import BottomNav from '../../../components/layout/BottomNav'; 14 | import Hero from '../../../components/layout/Hero'; 15 | 16 | const Main = ({ currentPageNum, tutorial, homework }) => { 17 | // Get current page data from data/pageData.js 18 | const currentPageData = pageData.find(page => page.order === currentPageNum); 19 | return ( 20 |
21 | 22 | {`CSS Grid PlayGround | ${currentPageData.title} | Mozilla`} 23 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 42 | 43 | 44 | 45 | 46 |

Firefox DevTools

47 |

Introduction to CSS Grid Layout

48 |
49 |
50 |
51 | 52 |
{tutorial}
53 |
54 | {homework ?
{homework}
: ''} 55 |
56 | 57 |
58 | ); 59 | }; 60 | 61 | Main.propTypes = { 62 | tutorial: PropTypes.node.isRequired, 63 | homework: PropTypes.node, 64 | currentPageNum: PropTypes.number.isRequired, 65 | }; 66 | 67 | Main.defaultProps = { 68 | homework: '', 69 | }; 70 | 71 | export default Main; 72 | -------------------------------------------------------------------------------- /src/pages/css-grid/components/img/fb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/css-grid/components/img/fb.png -------------------------------------------------------------------------------- /src/pages/css-grid/components/img/twitter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/css-grid/components/img/twitter.gif -------------------------------------------------------------------------------- /src/pages/css-grid/components/img/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/css-grid/components/img/twitter.png -------------------------------------------------------------------------------- /src/pages/css-grid/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | 4 | // components 5 | import Term from '../../components/Term'; 6 | import VideoPlayer from '../../components/VideoPlayer'; 7 | 8 | // images 9 | import line from '../../components/img/terms/line.svg'; 10 | import cell from '../../components/img/terms/cell.svg'; 11 | import area from '../../components/img/terms/area.svg'; 12 | import track from '../../components/img/terms/track.svg'; 13 | import row from '../../components/img/terms/row.svg'; 14 | import column from '../../components/img/terms/column.svg'; 15 | import gutter from '../../components/img/terms/gutter.svg'; 16 | 17 | const Tutorial = () => ( 18 |
19 | 20 |

Terminology

21 | 22 |

23 | Before we dive into CSS Grid concepts, let’s cover some basic terminology. 24 |

25 | 26 | 27 | The vertical and horizontal lines that divide the grid and separate the columns and rows. 28 | 29 | 30 | 31 | A single unit of a CSS grid. 32 | 33 | 34 | 35 | Rectangular space surrounded by four grid lines. A grid area can contain any number of grid 36 | cells. 37 | 38 | 39 | 40 | The space between two grid lines. This space can be horizontal or vertical 41 | 42 | 43 | 44 | A horizontal track of a grid. 45 | 46 | 47 | 48 | A vertical track of a grid. 49 | 50 | 51 | 52 | The space between rows and columns in a grid. 53 | 54 | 55 | Grid container 56 |

57 | The container that holds the entire CSS grid. It will be the element that has the 58 | display: grid or display: inline-grid property on it. 59 |

60 | 61 | Grid item 62 |

Any element that is a direct child of a grid container.

63 | 64 |

Got it? Let's move on to creating our first grid with CSS Grid Layout.

65 |
66 | ); 67 | 68 | export default () =>
} />; 69 | -------------------------------------------------------------------------------- /src/pages/debugger/02-check-variable-values.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | import DevHomework from '../../components/layout/DevHomework'; 4 | import CodeBlock from '../../components/CodeBlock'; 5 | import ImgCaption from '../../components/ImgCaption'; 6 | 7 | // images 8 | import add from './img/p2/add.gif'; 9 | import hover from './img/p2/hover.png'; 10 | import scopes from './img/p2/scopes.png'; 11 | import toolbar from './img/p2/toolbar.png'; 12 | import title from './img/p2/title.png'; 13 | import watch from './img/p2/watch.gif'; 14 | 15 | const Tutorial = () => ( 16 |
17 |

Find the Value of a Variable

18 | 19 |

20 | A good first step towards moving away from console.log is to tackle one of its primary use 21 | cases: finding the value of a variable. 22 |

23 | 24 |

Let’s take a look at a simple to-do app:

25 | 26 |

27 | 32 | Open to-do app in new tab. 33 | 34 |

35 | 36 |

37 | This app has a function called addTodo which will take the value of the input 38 | form, create an object, and then push that object onto an array of tasks. Let’s test it out by 39 | adding a new task. You’d expect to have this new task added to the list, but instead you see{' '} 40 | "[object HTMLInputElement]".{' '} 41 |

42 | 43 |

44 | Something is broken, and we need to debug the code. The temptation is to start adding 45 | console.log throughout the function, to pinpoint where the problem is. This approach might 46 | look something like this: 47 |

48 | 49 | 50 | {` 51 | const addTodo = e => { 52 | e.preventDefault(); 53 | const title = document.querySelector(".todo__input"); 54 | console.log('title is: ', title); 55 | const todo = { title }; 56 | console.log('todo is: ', todo'); 57 | 58 | items.push(todo); 59 | saveList(); 60 | console.log(‘The updated to-do list is: ‘, items); 61 | document.querySelector(".todo__add").reset(); 62 | }; 63 | `} 64 | 65 | 66 |

67 | This can work, but it is cumbersome and awkward. We also have to remember to remove these 68 | lines after fixing the code. There’s a much better way to do it with Debugger using what is 69 | called a breakpoint. 70 |

71 | 72 |

73 | A breakpoint is something that signals to Debugger that we wish to pause execution at a 74 | specific place in the code. While paused, we can view the value of a variable at that 75 | particular point in the code. Using the example linked above, let’s add a breakpoint to line 76 | 13. We can do this by clicking on the number 13 to the left of the code. If done correctly, a 77 | little blue flag will appear.{' '} 78 |

79 | 80 | 81 | 82 |

83 | Now try adding a task. Because of the breakpoint, execution of the code will pause inside the 84 | addTodo function, just before line 13 is executed. We want to track the value of 85 | the title and todo variables to see why the value of the input form 86 | isn’t being used when rendering the to-do list. There are a few ways we can check the value of 87 | a variable. 88 |

89 | 90 |

Method 1: Use the console

91 |

92 | When we hit a breakpoint, and pause execution, we can use the console within the scope of 93 | where the code was paused. This means we can type title into the console, hit 94 | enter, and view the value of that variable. 95 |

96 | 97 |

Method 2: Hover

98 |

99 | We can also hover over the variable in the source pane to view the value of that variable. Try 100 | hovering over other items such as an object or a function. Hovering provides us with quick 101 | access to all sorts of information that can be helpful for debugging. 102 |

103 | 104 | 105 |

Method 3: Scope Section

106 |

107 | At the bottom right of the Firefox Debugger is a section that displays all objects that are in 108 | scope at this point in the program. The first block will list variables in the current 109 | block/function and their values. 110 |

111 | 112 | 113 |

114 | If you tried any of those methods while paused at line 13, you may have noticed a little 115 | problem: the title and todo variables are undefined. This is because execution has paused just 116 | before line 13, and the lines that would assign a value to those variables haven’t been 117 | executed yet. We need to continue to move through the script until those variables are 118 | assigned a value. This is where the toolbar comes in. 119 |

120 | 121 | 122 |

Here is what the toolbar buttons do:

123 | 124 |
125 |
Play/Pause
126 |
127 | Pauses or resumes execution of the script we are debugging. When it displays a "play" icon, 128 | that means the script is paused, either because we’ve paused it with this button or because 129 | we’ve hit a breakpoint. 130 |
131 |
Step over
132 |
Steps across the current line of JavaScript code.
133 |
Step in
134 |
Steps into the function call on the current line of JavaScript code.
135 |
Step out
136 |
Runs the script until the current function exits.
137 |
138 | 139 |

140 | Right now, we are going to focus on the ‘Step Over’ button. This will step through the code 141 | one line at a time. If it comes across a function, it will execute the entire function. This 142 | is different from the ‘Step In’ button which will enter into that function instead of 143 | executing it. Don’t worry about understanding the difference just yet. We’ll cover ‘Step In’ 144 | more in the next section. 145 |

146 | 147 |

148 | Press the ‘Step Over’ button once. The code is still paused, but line 14 is now highlighted. 149 | This means line 13 has executed. The title variable is still undefined, but 150 | that is because we have paused just before line 14. Press ‘Step Over’ one more time and...now 151 | we are getting somewhere! We can now hover over title to get the value. We can 152 | also check the scope section.{' '} 153 |

154 | 155 | 160 | 161 |

162 | If we use one of the three methods to look at the value of title, we can spot our 163 | issue. The value isn’t a string like we’d expect, but rather it is an entire object! If we 164 | hover over title and scroll through its different properties, we’ll find that we 165 | should have referenced the ‘value’ property of the object instead of the entire object itself. 166 | Fix line 14 and the app will work properly. 167 |

168 |

169 | When we are done stepping through the code, we can press the 'Play/Pause' button which will 170 | resume execution of the script (until it hits another breakpoint). 171 |

172 |

173 | Phew. That was a lot of information. Try adding some other breakpoints in the app, and using 174 | the toolbar to step through the code. As you get comfortable with the basics of breakpoints 175 | and stepping through code, you will start to understand how powerful Debugger is. 176 |

177 |

178 | When you are ready, move on to the next section of the tutorial to learn more about how we can 179 | use Firefox Debugger to debug our code. 180 |

181 |
182 | ); 183 | 184 | const Homework = () => ( 185 | 186 |

187 | Another handy method for monitoring the value of a variable (or any expression) is to use the 188 | Watch Expressions section in the sidebar. You can set a watch expression by clicking “Add 189 | Watch Expression”, and typing the expression (in this case a variable), that you’d like to 190 | watch. Using the example above, try adding an expression for ‘title’ and another for ‘todo’. 191 | Set a breakpoint on line 13, add a new todo item to trigger the breakpoint, and start stepping 192 | through the function. When a value is assigned to the variable, the value will appear in the 193 | ‘Watch Expressions’ section. 194 |

195 | 196 | 202 |
203 | ); 204 | 205 | export default () =>
} homework={} />; 206 | -------------------------------------------------------------------------------- /src/pages/debugger/03-the-call-stack.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | import DevHomework from '../../components/layout/DevHomework'; 4 | import CodeBlock from '../../components/CodeBlock'; 5 | import ImgCaption from '../../components/ImgCaption'; 6 | 7 | // images 8 | import stepin from './img/p3/stepin.png'; 9 | import callstack from './img/p3/callstack.png'; 10 | import index from './img/p3/index.png'; 11 | import search from './img/p3/search.gif'; 12 | 13 | const Tutorial = () => ( 14 |
15 |

The Call Stack

16 | 17 |

18 | Now let’s add some functionality to our todo app. In this example we’ve added the ability to 19 | delete a task. Open the app in a new tab to follow along: 20 |

21 | 22 |

23 | 28 | Open to-do app in new tab. 29 | 30 |

31 | 32 |

33 | Try adding a few tasks. Now try deleting the first task. It seems to be working. Now try 34 | deleting the final task. Uh-Oh. Something is not quite right. The top task is deleted 35 | from the app every time, regardless of which task we try to delete. 36 |

37 | 38 |

39 | Let’s dive into Debugger to see what’s going on in the code. Locate the{' '} 40 | removeSingle 41 | function on Line 39 of app.js. This is the function that handles the removal of a task. Insert 42 | a breakpoint on line 40, which will trigger right after an item is deleted. Try to delete the 43 | last item in our to-do list to trigger this breakpoint. Now we can step through the code to 44 | find the root of our problem. 45 |

46 | 47 |

48 | In the previous section, we learned how to use the ‘Step Over’ button to step across lines of 49 | JavaScript code. We can use the 'Step Over’ button here, but when we reach the{' '} 50 | spliceItem function, it will execute the function and then move on to the next 51 | line. This is great, but what if the problem exists inside that spliceItem{' '} 52 | function? This is where we can use the ‘Step In’ button. 53 |

54 | 55 | 56 | 57 |

58 | The ‘Step In’ button is similar to the ‘Step Over’ Button, but it will allow us to step into a 59 | function for debugging, instead of executing it and moving on. Let’s use the ‘Step In’ button 60 | until we find ourselves on line 24, inside the createList function. Don’t worry 61 | if you are a bit confused about what is happening. It will all start to make sense. 62 |

63 | 64 |

65 | The createList function receives an object called list, and uses 66 | that object to create an updated to-do list. If we hover over the list object, we 67 | can see the list of to-do items that was provided to this function. We can also see that the 68 | item we intended to delete is still there, which means that this function was provided the 69 | wrong information. The bug is not in this function, but rather somewhere in the code before 70 | this function was invoked. 71 |

72 | 73 |

74 | On the right side of the Firefox Debugger, there is a section called{' '} 75 | Call Stack. This section provides us with information about which functions 76 | we have stepped into, what lines those function are on, and how deep we are in the call stack. 77 | If you are following along, you will see that there are four items in the call stack. We 78 | started at the spliceItem function, which invoked the saveList{' '} 79 | function, which invoked the{' '} 80 | createList function. 81 |

82 | 83 | 84 | 85 |

86 | The Call Stack section allows us to move back through the call stack so that we can figure out 87 | where this problem started. If we click on saveList in the call stack section, we 88 | can see that it is a simple (and probably unnecessary) function that takes the global{' '} 89 | items object and passes it to the createList function. The bug can’t 90 | be here so let’s click on the spliceItem function in the call stack. Do you spot 91 | the issue here? The index argument is undefined! Now we are getting somewhere. 92 |

93 | 94 | 99 | 100 |

101 | Let’s continue through the call stack to the removeSingle function. Look at the 102 | following: 103 |

104 | 105 | {` 106 | const index = el.dataset.number; 107 | `} 108 | 109 |

110 | If we hover over index (or number), we see that it is undefined. If 111 | we hover over dataset we’ll see that there is no number property. There is only{' '} 112 | index. A typo! The line should say: 113 |

114 | 115 | {` 116 | const index = el.dataset.index; 117 | `} 118 | 119 |

Once this is fixed, everything starts working.

120 |
121 | ); 122 | 123 | const Homework = () => ( 124 | 125 |

126 | It was easy to find the function in this example, but what if we had fifty functions across 127 | multiple files? Thankfully, you can search for a function in the source pane. Just hit{' '} 128 | shift + command +{' '} 129 | O on Mac or shift +{' '} 130 | control + O on Windows to 131 | bring up a search field in the source pane. You can search for a function or arrow through the 132 | provided list. 133 |

134 | 135 | 136 |
137 | ); 138 | 139 | export default () =>
} homework={} />; 140 | -------------------------------------------------------------------------------- /src/pages/debugger/04-conditional-breakpoints.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | import DevHomework from '../../components/layout/DevHomework'; 4 | import CodeBlock from '../../components/CodeBlock'; 5 | import ImgCaption from '../../components/ImgCaption'; 6 | 7 | // images 8 | import createbp from './img/p4/createbp.gif'; 9 | 10 | const Tutorial = () => ( 11 |
12 |

Conditional Breakpoints

13 |

14 | Breakpoints are a powerful tool for debugging JavaScript code, but what if we have a 15 | breakpoint inside a loop, or a function that is constantly being called? It can become tedious 16 | to have to constantly be resuming the execution of our code. It would be much better if we 17 | could control when a breakpoint is triggered. Lucky for us, Firefox DevTools has a useful 18 | feature called conditional breakpoints. 19 |

20 | 21 |

22 | A conditional breakpoint allows us to associate a condition with a breakpoint. When the 23 | program reaches a conditional breakpoint, the debugger pauses only if the condition 24 | provided evaluates to true. 25 |

26 | 27 |

28 | The best way to understand this, is with an example. Open up the to-do app below in a new tab 29 | and follow along. 30 |

31 | 32 |

33 | 38 | Open to-do app in new tab. 39 | 40 |

41 | 42 |

43 | In the first tutorial, we added a breakpoint inside the addTodo function. This 44 | breakpoint can be useful, but it will trigger every single time we add a to-do item. 45 |

46 | 47 |

48 | Let’s say we only want to trigger this breakpoint when the to-do item contains the word 49 | “turtle”. We can do this by right clicking on the line number we want to add a breakpoint to 50 | (in this case, line 24), and choosing “Add Conditional Breakpoint” from the menu. 51 |

52 | 53 | 54 | 55 |

We can now enter an expression. Try the following:

56 | 57 | 58 | {` 59 | title.indexOf("turtle") != -1; 60 | `} 61 | 62 | 63 |

64 | The indexOf() method will return -1 if the specified value (in this case, 65 | “turtle”) is not found. This means the expression provided to our conditional breakpoint 66 | will only evaluate to true if the string contains the word “turtle”. 67 |

68 | 69 |

70 | We can use any type of expression for a conditional breakpoint. We can even 71 | use console.log. A console.log expression will 72 | return undefined so it won’t pause execution, but it will still print to 73 | the console. Let’s try it out. 74 |

75 | 76 |

77 | Add a conditional breakpoint on line 68, right after we define the index{' '} 78 | variable. Enter the following as the condition: 79 |

80 | 81 | 82 | {` 83 | console.log(items[index].title); 84 | `} 85 | 86 | 87 |

88 | Now, every time we delete an item, the name of that item will be printed to the console. We 89 | get all of the benefits of using console.log, but we don’t have to worry about 90 | littering our code with lines that we will need to eventually delete. 91 |

92 |
93 | ); 94 | 95 | const Homework = () => ( 96 | 97 |

98 | Occasionally, we may find ourselves wanting to define our breakpoints in our code, rather than 99 | in the debugger. If we call debugger from our code, then Firefox Debugger will 100 | pause execution at that line. Here is an example: 101 |

102 | 103 | 104 | {` 105 | const addTodo = (e) => { 106 | debugger; 107 | e.preventDefault(); 108 | const title = document.querySelector('.todo__input').value; 109 | const todoForm = document.querySelector('.todo__add'); 110 | const todo = { 111 | title, 112 | done: false, 113 | }; 114 | 115 | items.push(todo); 116 | saveList(); 117 | todoForm.reset(); 118 | } 119 | `} 120 | 121 | 122 |

123 | Notice how a line has been added that says: debugger. This means we do not have 124 | to add the breakpoint from Firefox DevTools. 125 |

126 | 127 |

128 | Try it out.{' '} 129 | 134 | Open this link 135 | , launch Firefox Debugger, and add a task. Execution will be paused on that line because 136 | we defined that breakpoint in our code. 137 |

138 |
139 | ); 140 | 141 | export default () =>
} homework={} />; 142 | -------------------------------------------------------------------------------- /src/pages/debugger/05-learn-more.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | import DownloadLink from '../../components/DownloadLink'; 4 | 5 | const Tutorial = () => ( 6 |
7 |

Learn More

8 | 9 |

10 | Hopefully, through these tutorials and examples, you discovered how powerful the Firefox 11 | Debugger can be for debugging your JavaScript code. There is nothing wrong with using{' '} 12 | console.log, but it should just be one of the many tools in your toolbelt.{' '} 13 |

14 | 15 |

16 | If you want to learn more about Firefox Debugger, check out the{' '} 17 | 22 | Firefox Debugger Documentation on MDN 23 | . 24 |

25 | 26 |

27 | Mozilla recently rebuilt Firefox Debugger from the ground-up using React and Redux. This new 28 | Debugger is designed to be approachable, yet powerful. And the best part? It is completely 29 | hackable. It is available right now in Firefox Quantum.{' '} 30 | 35 | Check out the repository on GitHub 36 | . 37 |

38 | 39 |

40 | That's a wrap! If you haven't yet, check out Firefox Developer Edition. It has the latest 41 | features, is blazingly fast, and comes packed with all the development tools you need to build 42 | for the open web. 43 |

44 | 45 |

46 | Download Firefox Developer Edition 47 |

48 |
49 | ); 50 | 51 | export default () =>
} />; 52 | -------------------------------------------------------------------------------- /src/pages/debugger/components/_Main.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import PropTypes from 'prop-types'; 4 | 5 | // data 6 | import pageData from '../../../data/debugger'; 7 | 8 | // images 9 | import facebook from './img/fb.png'; 10 | 11 | // Components 12 | import Side from '../../../components/layout/Side'; 13 | import BottomNav from '../../../components/layout/BottomNav'; 14 | import Hero from '../../../components/layout/Hero'; 15 | 16 | const Main = ({ currentPageNum, tutorial, homework }) => { 17 | // Get current page data from data/pageData.js 18 | const currentPageData = pageData.find(page => page.order === currentPageNum); 19 | return ( 20 |
21 | 22 | {`Debugger PlayGround | ${currentPageData.title} | Mozilla`} 23 | 27 | 28 | {/* Facebook Meta */} 29 | 30 | 31 | 35 | 36 | 37 | {/* Twitter Meta */} 38 | 39 | 40 | 41 | 42 | 46 | 47 | 48 | 49 | 50 |

Firefox DevTools

51 |

Introduction to Debugger

52 |
53 |
54 |
55 | 56 |
{tutorial}
57 |
58 | {homework ?
{homework}
: ''} 59 |
60 | 61 |
62 | ); 63 | }; 64 | 65 | Main.propTypes = { 66 | tutorial: PropTypes.node.isRequired, 67 | homework: PropTypes.node, 68 | currentPageNum: PropTypes.number.isRequired, 69 | }; 70 | 71 | Main.defaultProps = { 72 | homework: '', 73 | }; 74 | 75 | export default Main; 76 | -------------------------------------------------------------------------------- /src/pages/debugger/components/img/fb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/debugger/components/img/fb.png -------------------------------------------------------------------------------- /src/pages/debugger/img/p1/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/debugger/img/p1/overview.png -------------------------------------------------------------------------------- /src/pages/debugger/img/p1/tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/debugger/img/p1/tools.png -------------------------------------------------------------------------------- /src/pages/debugger/img/p2/add.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/debugger/img/p2/add.gif -------------------------------------------------------------------------------- /src/pages/debugger/img/p2/hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/debugger/img/p2/hover.png -------------------------------------------------------------------------------- /src/pages/debugger/img/p2/scopes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/debugger/img/p2/scopes.png -------------------------------------------------------------------------------- /src/pages/debugger/img/p2/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/debugger/img/p2/title.png -------------------------------------------------------------------------------- /src/pages/debugger/img/p2/toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/debugger/img/p2/toolbar.png -------------------------------------------------------------------------------- /src/pages/debugger/img/p2/watch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/debugger/img/p2/watch.gif -------------------------------------------------------------------------------- /src/pages/debugger/img/p3/callstack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/debugger/img/p3/callstack.png -------------------------------------------------------------------------------- /src/pages/debugger/img/p3/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/debugger/img/p3/index.png -------------------------------------------------------------------------------- /src/pages/debugger/img/p3/search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/debugger/img/p3/search.gif -------------------------------------------------------------------------------- /src/pages/debugger/img/p3/stepin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/debugger/img/p3/stepin.png -------------------------------------------------------------------------------- /src/pages/debugger/img/p4/createbp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/debugger/img/p4/createbp.gif -------------------------------------------------------------------------------- /src/pages/debugger/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Main from './components/_Main'; 3 | import DevHomework from '../../components/layout/DevHomework'; 4 | import DownloadLink from '../../components/DownloadLink'; 5 | import ImgCaption from '../../components/ImgCaption'; 6 | 7 | // images 8 | import overview from './img/p1/overview.png'; 9 | import tools from './img/p1/tools.png'; 10 | 11 | const Tutorial = () => ( 12 |
13 |

Introduction to Firefox Debugger

14 |

15 | Are you only using console.log to debug your JavaScript code? It is OK to admit. 16 | Using console.log is easy and convenient, but it has limitations. It isn’t great 17 | for catching things as they happen, or diving deep into code to see exactly where it is 18 | broken. To do that, you’ll need a full-featured debugger. The Firefox Debugger has 19 | comprehensive tools to evaluate and repair broken code. Once you learn the basics, you’ll find 20 | that using a debugger is not only fast and easy, but it can save you from hours of 21 | frustration, and potential ‘bang-head-on-desk’ sessions. 22 |

23 |

24 | We’ll learn the basics of Firefox Debugger by debugging a simple to-do app. You will need 25 | Firefox to follow along. Don’t have Firefox? Check out{' '} 26 | Firefox Developer Edition. 27 |

28 |

29 | Before diving in, let’s take a look at the Debugger interface. Hit{' '} 30 | option + command +{' '} 31 | S on Mac or shift +{' '} 32 | ctrl + S on Windows to 33 | open the Debugger.{' '} 34 |

35 |

The Debugger is divided into three panes:

36 | 37 |

38 |

    39 |
  • 40 | The source list pane shows all the JavaScript files related to the 41 | current page or project. 42 |
  • 43 |
  • 44 | The source pane shows the content of those files. 45 |
  • 46 |
  • 47 | The tool pane contains info and tools. 48 |
  • 49 |
50 |

51 | 52 |

The tool pane can be broken down into five sections:

53 | 54 | 55 | 56 |

57 |

    58 |
  • 59 | The toolbar has buttons that control the debugger’s movement through the 60 | script. 61 |
  • 62 |
  • 63 | The watch expressions section allows us to watch expressions as 64 | executions are paused. 65 |
  • 66 |
  • 67 | The breakpoints section displays all of the breakpoints that have been 68 | set. Next to each breakpoint is a checkbox to enable or disable that breakpoint. 69 |
  • 70 |
  • 71 | The call stack section displays each level of the call stack, as well as 72 | the function name, filename, and line number. 73 |
  • 74 |
  • 75 | The scopes section displays all objects that are in scope. 76 |
  • 77 |
78 |

79 | 80 |

81 | Don’t worry if you don’t understand what all of these terms and symbols mean. We’ll cover them 82 | in the following sections. Just take note of where things are so that you can follow along. 83 |

84 |

Now that we’ve reviewed the Debugger interface, let’s use it to fix a broken to-do app.

85 |
86 | ); 87 | 88 | const Homework = () => ( 89 | 90 |

91 | Sometimes JavaScript code will be “minified” so that the file size is smaller and faster to 92 | load. This is great for your users, but it can make the code impossible to read in the 93 | Debugger. Thankfully, Firefox has a great option for viewing minified code. When you view a 94 | minified file in the source pane, an icon will appear at the bottom. After clicking this icon, 95 | Debugger will format the code into something that is human-readable, and display it as a new 96 | file in the source pane. 97 |

98 |

99 | Give it a try! The link below will bring you to a to-do app that is using a minified 100 | JavaScript file. Simply: 101 |

102 |
    103 |
  1. 104 | 109 | Visit this Link 110 | 111 |
  2. 112 |
  3. 113 | Open Debugger (option +{' '} 114 | command + S on Mac or{' '} 115 | shift + control +{' '} 116 | S on Windows) 117 |
  4. 118 |
  5. Find and click on app.js in the source list pane
  6. 119 |
  7. Locate and click on the format icon
  8. 120 |
121 |
122 | ); 123 | 124 | export default () =>
} homework={} />; 125 | -------------------------------------------------------------------------------- /src/pages/img/dino.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/img/dino.gif -------------------------------------------------------------------------------- /src/pages/img/ffdelogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/src/pages/img/ffdelogo.png -------------------------------------------------------------------------------- /src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | // components 4 | import Hero from '../components/layout/Hero'; 5 | import DownloadWhite from '../components/DownloadWhite'; 6 | import TutorialList from '../components/TutorialList'; 7 | import DownloadLink from '../components/DownloadLink'; 8 | 9 | // photos 10 | import logo from '../components/img/ff-logo.png'; 11 | 12 | const logoStyle = { 13 | width: '80px', 14 | }; 15 | const index = () => ( 16 |
17 | 18 | logo 19 |

Firefox DevTools Playground

20 |
21 |
22 |

23 | Learn, build, improve, and create with Firefox DevTools. 24 |

25 | 26 | 27 | 28 |
29 |
30 |
31 | 32 |
33 | ); 34 | 35 | export default index; 36 | -------------------------------------------------------------------------------- /src/styles/_config.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Configuration 3 | // ---------------------------------------------------------------------------- 4 | 5 | // Colors 6 | // ------------------------- 7 | 8 | $white : #fff; 9 | $black : #000; 10 | $primary : #306FFD; 11 | $primary-high : #1b7eff; 12 | $primary-low : #0069f2; 13 | $blue : #306FFD; 14 | $blue-dark : #2D68EE; 15 | $cyan : #2FCCE1; 16 | 17 | 18 | // Breakpoints 19 | // ------------------------- 20 | $breakpoints: ( 21 | xsmall: 450px, 22 | small: 750px, 23 | medium: 950px, 24 | large: 1200px 25 | ); 26 | 27 | // Import Fonts 28 | // ------------------------- 29 | @import url('https://fonts.googleapis.com/css?family=Open+Sans:400,700'); 30 | 31 | // Type 32 | // ------------------------- 33 | $base-font-size : 16px; 34 | $base-line-height : 1.5; 35 | $base-font-color : $black; 36 | $scale : 1.333; 37 | $leading : $base-line-height * 1rem; 38 | $sans-serif : 'Open Sans', 'Helvetica Neue', 'Helvetica', 'Arial', 'sans-serif'; 39 | $serif : 'Cambria', 'Georgia', 'serif'; 40 | $mono : 'Lucida Console', Monaco, monospace !default; 41 | $base-font-family : $sans-serif; 42 | $header-font-family : $sans-serif; 43 | $header-alt : 'Antonio', $sans-serif; 44 | $base-bold-weight : 600; 45 | $base-link-color : $primary; 46 | $base-link-hover : $primary-low; 47 | 48 | $unit: .5rem; 49 | 50 | 51 | // Defaults 52 | // ------------------------- 53 | 54 | $base-unit : .5rem; // if font-size is 16px, this should be 8px (8pt scale) 55 | $unit : $base-unit; 56 | $base-radius : 2px; 57 | $base-time : .3s; 58 | $base-ease : ease; 59 | 60 | // z-index 61 | // ------------------------- 62 | 63 | $z-index: ( 64 | bury: -1, 65 | content: 10, 66 | header: 20, 67 | navtrigger: 22, 68 | modal: 40, 69 | ); 70 | 71 | // Style 72 | // ------------------------- 73 | $body-bg : $white; 74 | -------------------------------------------------------------------------------- /src/styles/_examples.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // CSS for the examples on each page 3 | // ---------------------------------------------------------------------------- 4 | // becaues the examples are mant to be inspected, i wantd to keep clean classes, 5 | // and keep them cose to the example code. 6 | 7 | // GLOBAL 8 | ///////////// 9 | 10 | 11 | // 02 12 | .container-2 { 13 | display: grid; 14 | grid-template-columns: 150px 150px 150px; 15 | grid-template-rows: 150px 150px; 16 | grid-gap: 1rem; 17 | } 18 | 19 | // 04 20 | .container-4 { 21 | display: grid; 22 | width: 100%; 23 | grid-template-columns: repeat(3, 1fr); 24 | grid-template-rows: repeat(2, 150px); 25 | grid-gap: 1rem; 26 | } 27 | 28 | // 05 29 | .container-5 { 30 | width: 100%; 31 | display: grid; 32 | grid-template-columns: 100px 30% 1fr; 33 | grid-template-rows: 200px 100px; 34 | grid-gap: 1rem; 35 | } 36 | 37 | // 06 38 | .container-6 { 39 | display: grid; 40 | width: 100%; 41 | grid-template-columns: repeat(3, 1fr); 42 | grid-template-rows: repeat(2, 100px); 43 | grid-auto-rows: 150px; 44 | grid-gap: 1rem; 45 | } 46 | 47 | .item1 { 48 | grid-row: 2 / 3; 49 | grid-column: 2 / 3; 50 | } 51 | 52 | 53 | // 07 54 | .container-7 { 55 | display: grid; 56 | width: 100%; 57 | height: 600px; 58 | grid-template-columns: 200px 1fr 1fr; 59 | grid-template-rows: 80px 1fr 1fr 100px; 60 | grid-gap: 1rem; 61 | } 62 | 63 | .header-7 { 64 | grid-row: 1 / 2; 65 | grid-column: 1 / 4; 66 | } 67 | 68 | .sidebar-7 { 69 | grid-row: 2 / 4; 70 | grid-column: 1 / 2; 71 | } 72 | 73 | .content-1-7 { 74 | grid-row: 2 / 3; 75 | grid-column: 2 / 4; 76 | } 77 | 78 | .content-2-7 { 79 | grid-row: 3 / 4; 80 | grid-column: 2 / 3; 81 | } 82 | 83 | .content-3-7 { 84 | grid-row: 3 / 4; 85 | grid-column: 3 / 4; 86 | } 87 | 88 | .footer-7 { 89 | grid-row: 4 / 5; 90 | grid-column: 1 / 4; 91 | } 92 | 93 | 94 | // 08 95 | .container-8 { 96 | display: grid; 97 | width: 100%; 98 | height: 600px; 99 | grid-template-columns: 200px 1fr 1fr; 100 | grid-template-rows: 80px 1fr 1fr 100px; 101 | grid-gap: 1rem; 102 | grid-template-areas: "header header header" "sidebar content-1 content-1" "sidebar content-2 content-3" "footer footer footer"; 103 | } 104 | 105 | .header-8 { 106 | grid-area: header; 107 | } 108 | 109 | .sidebar-8 { 110 | grid-area: sidebar; 111 | } 112 | 113 | .content-1-8 { 114 | grid-area: content-1; 115 | } 116 | 117 | .content-2-8 { 118 | grid-area: content-2; 119 | } 120 | 121 | .content-3-8 { 122 | grid-area: content-3; 123 | } 124 | 125 | .footer-8 { 126 | grid-area: footer; 127 | } 128 | 129 | 130 | // 09 131 | .container-9 { 132 | display: grid; 133 | width: 100%; 134 | height: 600px; 135 | grid-gap: 1rem; 136 | grid-template-columns: [main-start sidebar-start] 200px [sidebar-end content-start] 1fr [column3-start] 1fr [content-end main-end]; 137 | grid-template-rows: [row1-start] 80px [row2-start] 1fr [row3-start] 1fr [row4-start] 100px [row4-end]; 138 | } 139 | 140 | .header-9 { 141 | grid-column: main-start / main-end; 142 | grid-row: row1-start / row2-start; 143 | } 144 | 145 | .sidebar-9 { 146 | grid-column: sidebar-start / sidebar-end; 147 | grid-row: row2-start / row4-start; 148 | } 149 | 150 | .content-1-9 { 151 | grid-column: content-start / content-end; 152 | grid-row: row2-start / row3-start; 153 | } 154 | 155 | .content-2-9 { 156 | grid-column: content-start / column3-start; 157 | grid-row: row3-start / row4-start; 158 | } 159 | 160 | .content-3-9 { 161 | grid-column: column3-start / content-end; 162 | grid-row: row3-start / row4-start; 163 | } 164 | 165 | .footer-9 { 166 | grid-column: main-start / main-end; 167 | grid-row: row4-start / row4-end; 168 | } 169 | 170 | 171 | 172 | // GLOBAL 173 | ///////////// 174 | .item { 175 | background-color: #1EAAFC; 176 | background-image: linear-gradient(130deg, #6C52D9 0%, #1EAAFC 85%, #3EDFD7 100%); 177 | box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 178 | 0 6px 6px rgba(0, 0, 0, 0.23); 179 | color: #fff; 180 | border-radius: 4px; 181 | border: 6px solid #171717; 182 | display: flex; 183 | justify-content: center; 184 | align-items: center; 185 | font-size: 18px; 186 | font-weight: bold; 187 | } 188 | 189 | .header-7, 190 | .header-8, 191 | .header-9 { 192 | background-color: #1EAAFC; 193 | background-image: linear-gradient(160deg, #6C52D9 0%, #9B8AE6 127%); 194 | } 195 | 196 | .sidebar-7, 197 | .sidebar-8, 198 | .sidebar-9 { 199 | background-image: linear-gradient(203deg, #3EDFD7 0%, #29A49D 90%) 200 | } 201 | 202 | .content-1-7, 203 | .content-1-8, 204 | .content-1-9, 205 | .content-2-7, 206 | .content-2-8, 207 | .content-2-9, 208 | .content-3-7, 209 | .content-3-8, 210 | .content-3-9, { 211 | background-image: linear-gradient(130deg, #6C52D9 0%, #1EAAFC 85%, #3EDFD7 100%); 212 | } 213 | 214 | .footer-7, 215 | .footer-8, 216 | .footer-9, { 217 | background-color: #6C52D9; 218 | background-image: linear-gradient(160deg, #6C52D9 0%, #9B8AE6 127%); 219 | } -------------------------------------------------------------------------------- /src/styles/_shame.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Shaaaaame 3 | // ---------------------------------------------------------------------------- 4 | // Anything here should be temporary 5 | 6 | 7 | // testing grids 8 | .test { 9 | border: 4px solid $black; 10 | background-color: rgba(0, 0, 0, 0.5); 11 | color: white; 12 | padding: 1rem; 13 | text-align: center; 14 | margin-bottom: 1rem; 15 | } 16 | 17 | .gallery { 18 | @include gallery(4 of 12); 19 | } -------------------------------------------------------------------------------- /src/styles/base/_global.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Global 3 | // ---------------------------------------------------------------------------- 4 | 5 | 6 | // Default Body Styles 7 | // ------------------- 8 | 9 | body { 10 | background-color: $body-bg; 11 | } 12 | 13 | 14 | // wrapper 15 | .wrapper { 16 | // height: 100vh; 17 | // overflow: hidden; 18 | } 19 | 20 | 21 | // Images 22 | // ------ 23 | 24 | img { 25 | max-width: 100%; 26 | height: auto; 27 | display: inline-block; 28 | vertical-align: middle; 29 | } 30 | 31 | 32 | // Removes Dotted Border 33 | // --------------------- 34 | 35 | :focus { 36 | outline: 0; 37 | } 38 | 39 | 40 | // Textarea takes on height automatically 41 | // -------------------------------------- 42 | 43 | textarea { 44 | height: auto; 45 | min-height: 50px; 46 | border-radius: $base-radius; 47 | } 48 | 49 | 50 | // Select elements are 100% width by default 51 | // ----------------------------------------- 52 | 53 | select { 54 | width: 100%; 55 | border-radius: $base-radius; 56 | } 57 | -------------------------------------------------------------------------------- /src/styles/base/_grid.scss: -------------------------------------------------------------------------------- 1 | // scss-lint:disable ColorVariable 2 | 3 | // ---------------------------------------------------------------------------- 4 | // Grids 5 | // ---------------------------------------------------------------------------- 6 | 7 | // Uses susy grid 3 for grid math and mappy-breakpoints for simple syntax 8 | // Some bootstrap-style helper classes included for rapid prototyping 9 | 10 | 11 | // Susy Configuration 12 | 13 | // Set # of columns 14 | $_columns: 12; 15 | 16 | // Susy Configuration 17 | $susy: ( 18 | 'columns': susy-repeat($_columns), 19 | 'gutters': .4, 20 | 'svg-grid-colors': hsla(180, 50%, 50%, 0.25), //scss-lint-ignore-line 21 | ); 22 | 23 | // Container mixin 24 | @mixin container($config: $susy, $debug: false) { 25 | $config: susy-settings($config); 26 | $container-spread: map-get($config, 'container-spread'); 27 | 28 | @include clearfix; 29 | margin: 0 auto; 30 | max-width: 100%; 31 | 32 | @if $debug { 33 | background: susy-svg-grid($grid: $config) no-repeat scroll; 34 | } 35 | } 36 | 37 | // Span Mixin 38 | @mixin span($span, $config: $susy, $center: false) { 39 | width: span($span, $config); 40 | 41 | // if last, then float right 42 | @if index($span, 'last') { 43 | float: right; 44 | } @else if $center == true { 45 | margin-left: auto; 46 | margin-right: auto; 47 | } @else { 48 | float: left; 49 | margin-right: gutter(); 50 | 51 | &:last-child { 52 | margin-right: 0; 53 | } 54 | } 55 | } 56 | 57 | // gallery mixin 58 | @mixin gallery($span, $config: ()) { 59 | $grid: susy-compile($span, $config); 60 | $span: map-get($grid, 'span'); 61 | $column-count: length(map-get($grid, 'columns')); 62 | $count: floor($column-count / $span); 63 | $spread: map-get($grid, 'spread') + 2; 64 | $container-spread: map-get($grid, 'container-spread') + 2; 65 | $extra: ($container-spread - $spread) * 0.5; 66 | $extra-push: su-call('su-gutter', $grid) * $extra; 67 | 68 | float: left; 69 | margin-right: -100%; 70 | 71 | @for $n from 1 through ($count) { 72 | $nth: unquote('#{$count}n + #{$n}'); 73 | $location: $span * ($n - 1) + 1; 74 | 75 | &:nth-child(#{$nth}) { 76 | $width: susy-compile($span at $location, $grid); 77 | width: su-call('su-span', $width); 78 | 79 | @if ($location > 1) { 80 | $wide: susy-compile('first' $location - 1 'wide', $grid); 81 | clear: none; 82 | margin-left: su-call('su-span', $wide) + $extra-push; 83 | } @else { 84 | clear: both; 85 | margin-left: if($extra-push > 0, $extra-push, 0); 86 | } 87 | } 88 | } 89 | } 90 | 91 | 92 | .row { 93 | @include clearfix; 94 | } 95 | 96 | .break { 97 | clear: both; 98 | } 99 | 100 | .container { 101 | @include clearfix; 102 | @include container($debug: false); 103 | height: 100%; 104 | margin: 0 auto; 105 | padding: 0 space(1); 106 | 107 | @include bp(small) { 108 | width: (map-get($breakpoints, small)); 109 | } 110 | 111 | @include bp(medium) { 112 | width: (map-get($breakpoints, medium)); 113 | } 114 | 115 | @include bp(large) { 116 | width: (map-get($breakpoints, large)); 117 | } 118 | } 119 | 120 | 121 | // Breakpoint specific column classes 122 | // ---------------------------------- 123 | 124 | // columns for x-small+ 125 | // ex: col-xs-8 or col-xs-3 126 | @for $i from 1 through $_columns { 127 | .col-xs-#{$i} { 128 | @include span($i); 129 | 130 | &--last { 131 | @include span($i last); 132 | } 133 | 134 | &--center { 135 | @include span($i, $center: true); 136 | } 137 | } 138 | } 139 | 140 | // columns for small+ 141 | // ex: col-sm-8 or col-sm-3 142 | @for $i from 1 through $_columns { 143 | .col-sm-#{$i} { 144 | @include bp(small) { 145 | @include span($i); 146 | 147 | &--last { 148 | @include span($i last); 149 | } 150 | 151 | &--center { 152 | @include span($i, $center: true); 153 | } 154 | } 155 | } 156 | } 157 | 158 | // columns for medium+ 159 | // ex: col-md-8 or col-md-3 160 | @for $i from 1 through $_columns { 161 | .col-md-#{$i} { 162 | @include bp(medium) { 163 | @include span($i); 164 | 165 | &--last { 166 | @include span($i last); 167 | } 168 | 169 | &--center { 170 | @include span($i, $center: true); 171 | } 172 | } 173 | } 174 | } 175 | 176 | // columns for large+ 177 | // ex: col-lg-8 or col-lg-3 178 | @for $i from 1 through $_columns { 179 | .col-lg-#{$i} { 180 | @include bp(large) { 181 | @include span($i); 182 | 183 | &--last { 184 | @include span($i last); 185 | } 186 | 187 | &--center { 188 | @include span($i, $center: true); 189 | } 190 | } 191 | } 192 | } 193 | 194 | 195 | // offset (push) columns for x-small+ 196 | // ex: col-xs-offset-8 or col-xs-offset-3 197 | @for $i from 1 through $_columns { 198 | .col-xs-offset-#{$i} { 199 | margin-left: span($i); 200 | } 201 | } 202 | 203 | // offset (push) columns for small+ 204 | // ex: col-sm-offset-8 or col-sm-offset-3 205 | @for $i from 1 through $_columns { 206 | .col-sm-offset-#{$i} { 207 | @include bp(small) { 208 | margin-left: span($i wide); 209 | } 210 | } 211 | } 212 | 213 | // offset (push) columns for medium+ 214 | // ex: col-md-offset-8 or col-md-offset-3 215 | @for $i from 1 through $_columns { 216 | .col-md-offset-#{$i} { 217 | @include bp(medium) { 218 | margin-left: span($i wide); 219 | } 220 | } 221 | } 222 | 223 | // offset (push) columns for large+ 224 | // ex: col-lg-offset-8 or col-lg-offset-3 225 | @for $i from 1 through $_columns { 226 | .col-lg-offset-#{$i} { 227 | @include bp(large) { 228 | margin-left: span($i wide); 229 | } 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/styles/base/_reset.scss: -------------------------------------------------------------------------------- 1 | * { 2 | border: 0; 3 | box-sizing: border-box; 4 | -webkit-font-smoothing: antialiased; 5 | font-weight: normal; 6 | margin: 0; 7 | outline: 0; 8 | padding: 0; 9 | text-decoration: none; 10 | text-rendering: optimizeLegibility; 11 | list-style: none; 12 | } 13 | 14 | html, 15 | body { 16 | min-height: 100vh; 17 | min-width: 100%; 18 | } 19 | 20 | a { 21 | color: currentColor; 22 | text-decoration: none; 23 | } 24 | 25 | textarea { 26 | resize: none; 27 | } 28 | 29 | ::selection { 30 | // background: $brand; 31 | // color: $white; 32 | } 33 | 34 | input, 35 | textarea { 36 | box-shadow: inset 0 1px 1px rgba(#000, .1); 37 | } 38 | -------------------------------------------------------------------------------- /src/styles/base/_type.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Type 3 | // ---------------------------------------------------------------------------- 4 | 5 | // Global Type 6 | // ----------- 7 | 8 | html, { 9 | color: $base-font-color; 10 | line-height: $base-line-height; 11 | font-family: $base-font-family; 12 | font-size: $base-font-size; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | } 16 | 17 | 18 | p { 19 | margin-bottom: space(2); 20 | } 21 | 22 | p.lead { 23 | @include typesize(1); 24 | font-weight: 300; 25 | } 26 | 27 | em, 28 | i { 29 | font-style: italic; 30 | line-height: inherit; 31 | } 32 | 33 | 34 | strong, 35 | b { 36 | font-weight: $base-bold-weight; 37 | } 38 | 39 | 40 | small, 41 | .small { 42 | @include typesize(-1); 43 | } 44 | 45 | 46 | // Headings 47 | // -------- 48 | 49 | h1, 50 | h2, 51 | h3, 52 | h4, 53 | h5, 54 | .h1, 55 | .h2, 56 | .h3, 57 | .h4, 58 | .h5 { 59 | font-family: $header-font-family; 60 | font-weight: $base-bold-weight; 61 | line-height: $base-line-height; 62 | } 63 | 64 | h1, 65 | .h1 { 66 | @include typesize(3); 67 | font-family: 'Antonio', $sans-serif; 68 | font-weight: 300; 69 | text-transform: uppercase; 70 | margin-bottom: space(2); 71 | line-height: $leading * 2; 72 | } 73 | 74 | h2, 75 | .h2 { 76 | @include typesize(2); 77 | margin-bottom: space(2); 78 | line-height: 2rem; 79 | font-family: 'Antonio', $sans-serif; 80 | font-weight: 300; 81 | text-transform: uppercase; 82 | border-bottom: 1px solid #eee; 83 | padding-bottom: space(5); 84 | margin-bottom: space(5); 85 | } 86 | 87 | h3, 88 | .h3 { 89 | @include typesize(1); 90 | font-family: 'Antonio', $sans-serif; 91 | font-weight: 700; 92 | text-transform: uppercase; 93 | margin-bottom: space(1); 94 | line-height: $leading; 95 | } 96 | 97 | h4, 98 | .h4 { 99 | @include typesize(0); 100 | margin-bottom: space(2); 101 | line-height: $leading; 102 | } 103 | 104 | h5, 105 | .h5 { 106 | @include typesize(-1); 107 | margin-bottom: space(1); 108 | line-height: $leading; 109 | } 110 | 111 | 112 | // Links 113 | // ----- 114 | 115 | a { 116 | color: $base-link-color; 117 | cursor: pointer; 118 | text-decoration: none; 119 | transition: color $base-time $base-ease; 120 | 121 | &:hover, 122 | &:focus { 123 | color: $base-link-hover; 124 | } 125 | 126 | img { 127 | border: 0; 128 | } 129 | 130 | } 131 | 132 | // Horizontal Rule 133 | // --------------- 134 | 135 | hr { 136 | height: 1px; 137 | background-color: lighten($black, 80%); 138 | border: 0; 139 | margin: space(2) 0; 140 | } 141 | 142 | 143 | // Lists 144 | // ------ 145 | 146 | $_list-side-margin: 1.25rem !default; 147 | $_list-line-height: inherit !default; 148 | 149 | ul, 150 | ol, 151 | dl { 152 | list-style-position: inside; 153 | line-height: $_list-line-height; 154 | margin-bottom: space(2); 155 | } 156 | 157 | ul, 158 | ol { 159 | list-style-type: disc; 160 | margin-left: $_list-side-margin; 161 | } 162 | 163 | ul.list{ 164 | li { 165 | list-style-type: disc; 166 | margin-left: space(2); 167 | } 168 | } 169 | 170 | ol.list { 171 | margin-left: space(4); 172 | 173 | li { 174 | list-style-type: number; 175 | margin-bottom: space(1); 176 | } 177 | } 178 | 179 | p code { 180 | background-color: #f3f3f3; 181 | padding: 0 2px; 182 | } 183 | 184 | 185 | 186 | // Blockquotes 187 | // ----------- 188 | 189 | blockquote { 190 | @include typesize(1); 191 | line-height: $base-line-height; 192 | margin: 0 0 spacing(1); 193 | padding: space(1) space(2); 194 | font-weight: 300; 195 | border-left: 5px solid lighten($black, 95%); 196 | } 197 | 198 | dt { 199 | float: left; 200 | clear: left; 201 | width: 100px; 202 | text-align: right; 203 | font-weight: bold; 204 | } 205 | 206 | dt::after { 207 | content: ":"; 208 | } 209 | 210 | dd { 211 | margin: 0 0 0 110px; 212 | padding: 0 0 space(1) 0; 213 | } 214 | 215 | .shortcut { 216 | background: #fff; 217 | border-radius: 5px; 218 | border: 1px solid #D6DADC; 219 | border-bottom-color: #CDD2D4; 220 | box-shadow: 0 2px 3px rgba(0, 0, 0, .1); 221 | color: #4d4d4d; 222 | cursor: default; 223 | display: inline-block; 224 | font-size: 14px; 225 | padding: 0px 8px; 226 | font-weight: bold; 227 | } 228 | -------------------------------------------------------------------------------- /src/styles/components/_404.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // 404 3 | // ---------------------------------------------------------------------------- 4 | 5 | .not-found__dino { 6 | height: 300px; 7 | @include bp(medium) { 8 | @include span(6); 9 | } 10 | } 11 | 12 | .not-found__dino img { 13 | height: 300px; 14 | margin-left: auto; 15 | margin-right: auto; 16 | display: block; 17 | 18 | @include bp(medium) { 19 | float: right; 20 | margin: 0; 21 | 22 | } 23 | } 24 | 25 | .not-found__text { 26 | color: black; 27 | text-align: center; 28 | margin-top: space(3); 29 | 30 | @include bp(medium) { 31 | @include span(6); 32 | text-align: left; 33 | display: flex; 34 | flex-direction: column; 35 | height: 300px; 36 | justify-content: center; 37 | margin-top: 0; 38 | } 39 | } -------------------------------------------------------------------------------- /src/styles/components/_buttons.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Buttons 3 | // ---------------------------------------------------------------------------- 4 | 5 | /** 6 | This is a system for easiliy creating buttons, and spinning up new button 7 | styles. 8 | This uses a mixin that can be found at /util/mixins/_buttons.scss 9 | */ 10 | 11 | 12 | // Base button styles 13 | // ------------------ 14 | 15 | button, 16 | .button { 17 | @include typesize(0); 18 | display: inline-block; 19 | font-weight: inherit; 20 | border-radius: $base-radius; 21 | line-height: inherit; 22 | text-align: center; 23 | white-space: nowrap; 24 | vertical-align: middle; 25 | cursor: pointer; 26 | user-select: none; 27 | padding: spacing(.5) spacing(1); 28 | border: 2px solid transparent; 29 | background: $primary; 30 | transition: all $base-time $base-ease; 31 | text-decoration: none; 32 | 33 | 34 | &:active, 35 | &:hover, 36 | &:focus { 37 | background: darken($primary, 20%); 38 | text-decoration: none; 39 | outline: 0; 40 | background-image: none; 41 | } 42 | 43 | &:disabled { 44 | cursor: not-allowed; 45 | opacity: .65; 46 | box-shadow: none; 47 | } 48 | } 49 | 50 | .button-download { 51 | display: block; 52 | margin-bottom: space(2); 53 | background-color: #306efe; 54 | border-radius: 100px; 55 | color: $white; 56 | padding: space(2) space(4); 57 | border: none; 58 | transition: all .3s ease; 59 | font-weight: bold; 60 | 61 | &:hover { 62 | background-color: darken(#306efe, 10%); 63 | } 64 | } 65 | 66 | // Variables 67 | // --------- 68 | 69 | $_button-primary-color: $white; 70 | $_button-primary-color-hover: $white; 71 | $_button-primary-bg: $primary; 72 | $_button-primary-bg-hover: darken($primary, 10%); 73 | $_button-primary-border: $primary; 74 | $_button-primary-border-hover: darken($primary, 10%); 75 | 76 | $_button-secondary-color: $white; 77 | $_button-secondary-color-hover: $white; 78 | $_button-secondary-bg: #1a1a1a; 79 | $_button-secondary-bg-hover: lighten(#1a1a1a, 10%); 80 | 81 | 82 | /** 83 | Main Buttons 84 | Copied from buttons mixin. You can create a new button like so: 85 | .button--modifier { 86 | @include button-variant($color, color-hover, background, $background-hover, $border, $border-hover) 87 | } 88 | Note that $background-hover, $border, & $border-hover are optional 89 | Takes a few variables and creates a new button style 90 | @param {color}: color of button text 91 | @param {color-hover} color of button text on hover 92 | @param {background}: color of button background 93 | @param {background-hover} color of button on hover 94 | @param {border} color of button border (defaults to background color) 95 | @param {border-hover} color of border on hover (defaults to hover bg color) 96 | */ 97 | 98 | // Button Variations 99 | // ------------------ 100 | 101 | .button--primary { 102 | @include button-variant( 103 | $_button-primary-color, 104 | $_button-primary-color-hover, 105 | $_button-primary-bg, 106 | $_button-primary-bg-hover, 107 | $_button-primary-border, 108 | $_button-primary-border-hover 109 | ); 110 | } 111 | 112 | .button--secondary { 113 | @include button-variant( 114 | $_button-secondary-color, 115 | $_button-secondary-color-hover, 116 | $_button-secondary-bg, 117 | $_button-secondary-bg-hover 118 | ); 119 | } 120 | 121 | 122 | /** 123 | Outline Buttons 124 | Takes a few variables and creates a new button style 125 | Copied from buttons mixin. You can create a new button like so: 126 | .button-outline--modifier { 127 | @include($color, $border, $background-hover, $color-hover, $border-hover) 128 | } 129 | only first param is required 130 | @param {color}: color of button text 131 | @param {border} color of button outline/border (default: $color) 132 | @param {background-hover}: color of background on hover (default: $color) 133 | @param {color-hover} color of text on hover (default: $white) 134 | @param {border-hover} color of border on hover (default: $background-hover) 135 | */ 136 | 137 | // Outline Button Variations 138 | // ---------------------- 139 | 140 | .button-outline--primary { 141 | @include button-outline-variant($_button-primary-bg); 142 | } 143 | 144 | .button-outline--secondary { 145 | @include button-outline-variant($_button-secondary-bg); 146 | } 147 | 148 | 149 | 150 | // Button Sizes 151 | // ------------ 152 | 153 | .button--large { 154 | @include typesize(1); 155 | padding: .75rem 1.25rem; 156 | } 157 | 158 | .button--small { 159 | @include typesize(-1); 160 | padding: .25rem .75rem; 161 | } 162 | 163 | 164 | 165 | // Block button 166 | // ------------ 167 | 168 | .button--block { 169 | display: block; 170 | width: 100%; 171 | } 172 | 173 | // Vertically space out multiple block buttons 174 | .button--block + .button--block { 175 | margin-top: spacing(1); 176 | } 177 | 178 | // Specificity overrides 179 | input[type="submit"], 180 | input[type="reset"], 181 | input[type="button"] { 182 | &.button--block { 183 | width: 100%; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/styles/components/_code-block.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Code Block 3 | // ---------------------------------------------------------------------------- 4 | 5 | .code-block { 6 | background-color: #F4F7FE; 7 | padding: 0 space(4); 8 | border-left: space(1) solid #306FFD; 9 | overflow: scroll; 10 | margin-bottom: space(4); 11 | margin-top: space(4); 12 | color: $black; 13 | } -------------------------------------------------------------------------------- /src/styles/components/_download-button.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Download Button 3 | // ---------------------------------------------------------------------------- 4 | 5 | 6 | .download-button { 7 | border: 2px solid $white; 8 | color: $white; 9 | background-color: transparent; 10 | font-size: 14px; 11 | font-weight: bold; 12 | padding: 0 space(1); 13 | line-height: 35px; 14 | transition: background-color .2s ease; 15 | 16 | &:hover { 17 | background-color: rgba(255, 255, 255, 0.2); 18 | } 19 | 20 | &.float-left { 21 | float: left; 22 | } 23 | 24 | &.float-right { 25 | float: right; 26 | } 27 | } 28 | 29 | .download-button__icon { 30 | margin-right: space(1); 31 | } -------------------------------------------------------------------------------- /src/styles/components/_download-white.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Download Button (White) 3 | // ---------------------------------------------------------------------------- 4 | .download-white { 5 | @include typesize(0); 6 | border: 0px solid $white; 7 | color: $primary; 8 | background-color: $white; 9 | border-radius: 50px; 10 | font-weight: bold; 11 | padding: space(2) space(3); 12 | box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.2); 13 | transition: box-shadow .2s ease; 14 | font-weight: bold; 15 | &:hover, 16 | &:focus, 17 | &:active { 18 | background-color: $white; 19 | box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.7); 20 | } 21 | &.float-left { 22 | float: left; 23 | } 24 | &.float-right { 25 | float: right; 26 | } 27 | 28 | img { 29 | margin-right: 10px; 30 | max-width: none; 31 | height: 14px; 32 | } 33 | } 34 | 35 | .download-button__icon { 36 | margin-right: space(1); 37 | } -------------------------------------------------------------------------------- /src/styles/components/_hero.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Hero 3 | // ---------------------------------------------------------------------------- 4 | 5 | 6 | .hero { 7 | padding: space(10) 0 space(18); 8 | background-color: $blue; 9 | background-image: linear-gradient(60deg, #306EFE 0%, #003AB7); 10 | text-align: center; 11 | color: $white; 12 | position: relative; 13 | 14 | h1 { 15 | margin: 0; 16 | } 17 | 18 | h3 { 19 | display: inline-block; 20 | margin-bottom: space(4); 21 | } 22 | 23 | h3::after { 24 | content: " "; 25 | border-bottom: 3px solid $white; 26 | display: block; 27 | width: 100%; 28 | margin-top: space(1); 29 | } 30 | } 31 | 32 | .hero__breadcrumb { 33 | float: left; 34 | font-weight: bold; 35 | margin-top: -1 * space(8); 36 | cursor: pointer; 37 | font-size: 14px; 38 | opacity: .7; 39 | transition: opacity $base-time $base-ease; 40 | 41 | span { 42 | color: $white; 43 | font-weight: bold; 44 | margin-left: space(1); 45 | } 46 | 47 | &:hover { 48 | opacity: 1; 49 | } 50 | } 51 | 52 | .hero--gray { 53 | background-color: #ECF1FC; 54 | background-image: none; 55 | text-align: left; 56 | } 57 | 58 | .hero--gray .hero__breadcrumb { 59 | 60 | span { 61 | color: black; 62 | } 63 | 64 | img { 65 | display: none; 66 | } 67 | } 68 | .hero__div { 69 | position: absolute; 70 | bottom: 0; 71 | left: 0; 72 | width: 100%; 73 | height: auto; 74 | margin-bottom: -1px; 75 | } -------------------------------------------------------------------------------- /src/styles/components/_img-caption.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Image (with caption) 3 | // ---------------------------------------------------------------------------- 4 | 5 | 6 | .img-caption__img { 7 | margin-bottom: 0 !important; 8 | } 9 | 10 | .img-caption__text { 11 | width: 100%; 12 | display: block; 13 | background-color: #ebebeb; 14 | margin-bottom: space(3); 15 | text-align: center; 16 | padding: space(1); 17 | font-size: 14px; 18 | } 19 | 20 | .img-caption--dark { 21 | .img-caption__text { 22 | background-color: #282828; 23 | color: $white; 24 | } 25 | } -------------------------------------------------------------------------------- /src/styles/components/_term.scss: -------------------------------------------------------------------------------- 1 | .term { 2 | @include clearfix; 3 | margin-bottom: space(3); 4 | } 5 | 6 | .term__img { 7 | float: left; 8 | @include span(4); 9 | } 10 | 11 | .term__text { 12 | @include span(8); 13 | padding-left: space(2); 14 | float: left; 15 | padding-top: space(2); 16 | } 17 | 18 | .term__name { 19 | font-weight: bold; 20 | } -------------------------------------------------------------------------------- /src/styles/components/_tutorial-list.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Tutorial List 3 | // ---------------------------------------------------------------------------- 4 | 5 | .tutorial-list { 6 | padding: space(8) 0 space(16); 7 | } 8 | 9 | .tutorial-list__title { 10 | border-bottom: 0px solid transparent; 11 | padding-bottom: 0; 12 | margin-bottom: space(12); 13 | text-align: center; 14 | } 15 | 16 | .tutorial-item { 17 | margin-bottom: space(8); 18 | 19 | @include bp(medium) { 20 | margin-bottom: 0; 21 | } 22 | } 23 | 24 | .tutorial-item__img { 25 | margin-bottom: space(3); 26 | transition: opacity $base-time $base-ease; 27 | 28 | &:hover { 29 | opacity: .8; 30 | } 31 | } 32 | 33 | .tutorial-item__title { 34 | border-bottom: 0px solid transparent; 35 | margin-bottom: space(3); 36 | padding-bottom: 0; 37 | } 38 | 39 | .tutorial-item__btn { 40 | background-color: transparent; 41 | border: 2px solid $primary; 42 | border-radius: 20px; 43 | padding: space(1) space(3); 44 | font-weight: bold; 45 | color: $blue; 46 | margin-top: space(2); 47 | 48 | img { 49 | margin-left: space(1); 50 | } 51 | 52 | &:hover { 53 | color: $blue; 54 | background-color: lighten($blue, 35%); 55 | } 56 | } -------------------------------------------------------------------------------- /src/styles/components/_video.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Video Player 3 | // ---------------------------------------------------------------------------- 4 | 5 | .video-player { 6 | border-bottom: 1px solid #eee; 7 | margin-bottom: space(4); 8 | cursor: pointer; 9 | } 10 | .video-player__text { 11 | border-bottom: 0px transparent; 12 | margin-bottom: space(0); 13 | display: inline-block; 14 | transition: opacity .3s ease; 15 | 16 | &:hover { 17 | opacity: .8; 18 | } 19 | } 20 | 21 | .video-player__icon { 22 | height: space(4); 23 | margin-left: space(1); 24 | vertical-align: middle; 25 | top: 3px; 26 | position: relative; 27 | opacity: .6; 28 | } 29 | 30 | // fixing package css 31 | .modal-video-close-btn:hover { 32 | background-color: transparent; 33 | } -------------------------------------------------------------------------------- /src/styles/examples/02.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: grid; 3 | grid-template-columns: 150px 150px 150px; 4 | grid-template-rows: 150px 150px; 5 | grid-gap: 1rem; 6 | } 7 | 8 | .item { 9 | background-color: #1EAAFC; 10 | background-image: linear-gradient(130deg, #6C52D9 0%, #1EAAFC 85%, #3EDFD7 100%); 11 | box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23); 12 | color: #fff; 13 | border-radius: 4px; 14 | border: 6px solid #171717; 15 | } 16 | -------------------------------------------------------------------------------- /src/styles/examples/04.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: grid; 3 | width: 100%; 4 | grid-template-columns: repeat(3, 1fr); 5 | grid-template-rows: repeat(2, 150px); 6 | grid-gap: 1rem; 7 | } 8 | 9 | .item { 10 | background-color: #1EAAFC; 11 | background-image: linear-gradient(130deg, #6C52D9 0%, #1EAAFC 85%, #3EDFD7 100%); 12 | box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); 13 | color: #fff; 14 | border-radius: 4px; 15 | border: 6px solid #171717; 16 | } -------------------------------------------------------------------------------- /src/styles/layout/_bottom-nav.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Bottom Nav 3 | // ---------------------------------------------------------------------------- 4 | 5 | .bottom-nav { 6 | background-color: $blue; 7 | padding: space(4) 0; 8 | // height: 144px; 9 | background-color: $blue; 10 | background-image: linear-gradient(90deg, #2F6CFC 0%, #0039B7); 11 | } 12 | 13 | .bottom-nav__item { 14 | width: 50%; 15 | float: left; 16 | padding: 0 space(6); 17 | height: space(12); 18 | display: flex; 19 | align-items: center; 20 | 21 | &--right { 22 | border-left: 1px solid rgba(255, 255, 255, 0.2); 23 | justify-content: flex-start; 24 | } 25 | 26 | &--left { 27 | text-align: right; 28 | justify-content: flex-end; 29 | } 30 | 31 | @include bp(medium) { 32 | padding: 0 space(12); 33 | } 34 | } 35 | 36 | .bottom-nav__inner { 37 | cursor: pointer; 38 | color: $white; 39 | } 40 | 41 | .bottom-nav__lead { 42 | display: block; 43 | } 44 | 45 | .bottom-nav__title { 46 | @include typesize(1); 47 | display: block; 48 | font-family: 'Antonio', $sans-serif; 49 | text-transform: uppercase; 50 | transition: border-bottom .2s ease; 51 | border-bottom: 2px solid transparent; 52 | 53 | &:hover { 54 | border-bottom: 2px solid $white; 55 | } 56 | 57 | @include bp(medium) { 58 | @include typesize(2); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/styles/layout/_cta.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Bottom CTA 3 | // ---------------------------------------------------------------------------- 4 | 5 | .cta { 6 | padding: space(8) 0; 7 | background-color: blue; 8 | background-color: #0727A9; 9 | background-image: linear-gradient(90deg, #0727A9 0%, #0772AC); 10 | } 11 | 12 | .cta__content { 13 | display: flex; 14 | flex-direction: column; 15 | align-items: center; 16 | 17 | @include bp(medium) { 18 | flex-direction: row; 19 | } 20 | } 21 | 22 | .cta__logo { 23 | margin-bottom: space(4); 24 | 25 | @include bp(medium) { 26 | flex: 1 0 0; 27 | display: flex; 28 | justify-content: center; 29 | margin-bottom: 0; 30 | } 31 | } 32 | 33 | .cta__text { 34 | @include typesize(3); 35 | text-transform: uppercase; 36 | line-height: space(8); 37 | font-family: $header-alt; 38 | color: $white; 39 | text-align: center; 40 | margin-bottom: space(4); 41 | 42 | @include bp(medium) { 43 | flex: 2 0 0; 44 | text-align: left; 45 | margin-bottom: 0; 46 | padding: 0 space(2); 47 | } 48 | 49 | @include bp(large) { 50 | @include typesize(4); 51 | } 52 | } 53 | 54 | .cta__download { 55 | @include bp(medium) { 56 | display: flex; 57 | justify-content: center; 58 | flex: 1 0 0; 59 | } 60 | } -------------------------------------------------------------------------------- /src/styles/layout/_footer.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Footer 3 | // ---------------------------------------------------------------------------- 4 | 5 | .footer { 6 | @include clearfix; 7 | padding: space(8); 8 | background-color: $black; 9 | color: $white; 10 | } 11 | 12 | .footer__logo { 13 | height: space(4); 14 | } 15 | 16 | .footer__header { 17 | font-weight: bold; 18 | margin-bottom: space(2); 19 | margin-top: space(4); 20 | display: block; 21 | 22 | @include bp(medium) { 23 | margin-top: 0; 24 | } 25 | } 26 | 27 | .footer__list { 28 | margin: 0; 29 | 30 | a { 31 | color: $white; 32 | } 33 | 34 | a:hover { 35 | color: darken($white, 10%); 36 | } 37 | 38 | li { 39 | margin-bottom: space(1); 40 | } 41 | } 42 | 43 | .footer__div { 44 | background-color: #3e3e3e; 45 | } 46 | 47 | .footer__legal { 48 | @include typesize(-1); 49 | margin: 0; 50 | 51 | li { 52 | float: left; 53 | margin-right: space(3); 54 | } 55 | 56 | a { 57 | color: $white; 58 | } 59 | } -------------------------------------------------------------------------------- /src/styles/layout/_header.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Header 3 | // ---------------------------------------------------------------------------- 4 | 5 | .header { 6 | background-color: $blue-dark; 7 | background-image: linear-gradient(60deg, darken(#306EFE, 6%) 0%, darken(#003AB7, 6%)); 8 | padding: space(3) 0; 9 | } 10 | 11 | .header__logo { 12 | float: left; 13 | display: inline-block; 14 | height: 35px; 15 | } 16 | 17 | .header__cta { 18 | float: right; 19 | border: 2px solid $white; 20 | color: $white; 21 | background-color: transparent; 22 | font-size: 14px; 23 | font-weight: bold; 24 | padding: 0 space(1); 25 | line-height: 35px; 26 | transition: background-color .2s ease; 27 | 28 | &:hover { 29 | background-color: rgba(255, 255, 255, 0.2); 30 | } 31 | } 32 | 33 | .header__cta-download { 34 | margin-right: space(1); 35 | } -------------------------------------------------------------------------------- /src/styles/layout/_homework.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Homework 3 | // ---------------------------------------------------------------------------- 4 | 5 | .homework { 6 | background-color: #1F2534; 7 | padding: space(4); 8 | color: white; 9 | text-align: center; 10 | } 11 | 12 | .homework__logo { 13 | height: 85px; 14 | display: block; 15 | margin-bottom: space(4); 16 | margin-left: auto; 17 | margin-right: auto; 18 | } 19 | 20 | .homework__title { 21 | border-bottom: 0; 22 | line-height: space(6); 23 | display: inline-block; 24 | margin-bottom: space(4); 25 | padding-bottom: 0; 26 | 27 | &::after { 28 | content: " "; 29 | border-bottom: 2px solid #306FFD; 30 | display: block; 31 | width: 100%; 32 | margin-top: space(1); 33 | } 34 | } 35 | 36 | .homework__content { 37 | text-align: left; 38 | 39 | p code { 40 | background-color: $black; 41 | } 42 | 43 | a { 44 | color: white; 45 | text-decoration: underline; 46 | } 47 | 48 | @include bp(medium) { 49 | @include span(8); 50 | margin-left: span(2); 51 | } 52 | } -------------------------------------------------------------------------------- /src/styles/layout/_layout.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // General Layout 3 | // ---------------------------------------------------------------------------- 4 | 5 | .main { 6 | padding-top: space(6); 7 | min-height: calc(100vh - 380px); 8 | } 9 | 10 | .content { 11 | padding-bottom: space(6); 12 | overflow-y: scroll; 13 | 14 | img { 15 | margin-bottom: space(3); 16 | } 17 | 18 | @include bp(medium) { 19 | @include span(8); 20 | padding-left: space(3); 21 | } 22 | 23 | @include bp(large) { 24 | @include span(9); 25 | padding-left: space(9); 26 | } 27 | } -------------------------------------------------------------------------------- /src/styles/layout/_side.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Side 3 | // ---------------------------------------------------------------------------- 4 | 5 | $_circle-radius: 40px; 6 | $_circle-color-done: #C0D0F3; 7 | $_circle-color: #EEEEEE; 8 | $_circle-active-color-1: #FFC73D; 9 | $_circle-active-color-2: #FF722A; 10 | 11 | .side { 12 | @include span(4); 13 | border-right: 1px solid #EDEDED; 14 | padding-bottom: space(4); 15 | display: none; 16 | 17 | @include bp(medium) { 18 | display: block; 19 | } 20 | 21 | @include bp(large) { 22 | @include span(3); 23 | } 24 | } 25 | 26 | .side-nav__circle { 27 | float: left; 28 | background-color: #EEEEEE; 29 | height: $_circle-radius; 30 | width: $_circle-radius; 31 | border-radius:50%; 32 | display: flex; 33 | align-items: center; 34 | justify-content: center; 35 | transition: background-color .2s ease; 36 | 37 | .is-active & { 38 | background-color: $_circle-active-color-1; 39 | background-image: linear-gradient(45deg, $_circle-active-color-1 0%, $_circle-active-color-2 99%); 40 | } 41 | 42 | .is-done & { 43 | background-color: $_circle-color-done; 44 | } 45 | } 46 | 47 | 48 | 49 | .side-nav__circle-inner { 50 | background-color: $white; 51 | height: $_circle-radius - 12px; 52 | width: $_circle-radius - 12px; 53 | border-radius: 50%; 54 | display: flex; 55 | justify-content: center; 56 | 57 | img { 58 | margin-top: 2px; 59 | } 60 | } 61 | 62 | .side-nav__page { 63 | margin-left: space(3); 64 | line-height: $_circle-radius; 65 | color: $black; 66 | } 67 | 68 | .is-active .side-nav__page { 69 | font-weight: bold; 70 | } 71 | 72 | .side-nav__line { 73 | background-color: $_circle-color-done; 74 | width: 4px; 75 | height: space(3); 76 | border-radius: 2px; 77 | margin-left: ($_circle-radius / 2) - 2; 78 | margin-top: space(1); 79 | margin-bottom: space(1); 80 | visibility: hidden; 81 | 82 | &.is-active { 83 | visibility: visible; 84 | } 85 | } 86 | 87 | .side-nav__wrap { 88 | height: $_circle-radius; 89 | cursor: pointer; 90 | 91 | &:hover { 92 | .side-nav__circle { 93 | background-color: darken($_circle-color, 10%); 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /src/styles/style.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Master Style Sheet 3 | // ---------------------------------------------------------------------------- 4 | 5 | // Configuration 6 | // ------------------------- 7 | @import 'config'; 8 | 9 | // Vendor 10 | // ------------------------- 11 | @import '../../node_modules/susy/sass/susy'; 12 | @import '../../node_modules/susy/sass/plugins/svg-grid'; 13 | @import '../../node_modules/mappy-breakpoints/mappy-breakpoints'; 14 | 15 | // Utilities 16 | // ------------------------- 17 | @import 'util/mixins/clearfix'; 18 | @import 'util/mixins/type'; 19 | @import 'util/mixins/buttons'; 20 | @import 'util/mixins/z-index'; 21 | @import 'util/functions/spacing'; 22 | @import 'util/functions/math'; 23 | @import 'util/functions/convert'; 24 | @import 'util/whitespace'; 25 | @import 'util/util'; 26 | 27 | 28 | // Base 29 | // ------------------------- 30 | @import 'base/reset'; 31 | @import 'base/type'; 32 | @import 'base/grid'; 33 | @import 'base/global'; 34 | 35 | // Layout 36 | // ------------------------- 37 | @import 'layout/layout'; 38 | @import 'layout/header'; 39 | @import 'layout/side'; 40 | @import 'layout/footer'; 41 | @import 'layout/bottom-nav'; 42 | @import 'layout/homework'; 43 | @import 'layout/cta'; 44 | 45 | // Components 46 | // ------------------------- 47 | @import 'components/buttons'; 48 | @import 'components/hero'; 49 | @import 'components/code-block'; 50 | @import 'components/term'; 51 | @import 'components/download-button'; 52 | @import 'components/download-white'; 53 | @import 'components/tutorial-list'; 54 | @import 'components/404'; 55 | @import 'components/img-caption'; 56 | @import 'components/video'; 57 | 58 | 59 | // Examples 60 | // ------------------------- 61 | @import 'examples'; 62 | 63 | // Shaaaaame 64 | // ------------------------- 65 | @import 'shame'; 66 | -------------------------------------------------------------------------------- /src/styles/util/_util.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Utilities 3 | // ---------------------------------------------------------------------------- 4 | 5 | 6 | // Floats 7 | // ------------------------- 8 | 9 | .clearfix { 10 | @include clearfix; 11 | } 12 | 13 | .center { 14 | margin-left: auto; 15 | margin-right: auto; 16 | } 17 | 18 | .pull-right { 19 | float: right; 20 | } 21 | 22 | .pull-left { 23 | float: left; 24 | } 25 | 26 | // align 27 | .text-center { 28 | text-align: center; 29 | } 30 | 31 | 32 | // Visibility 33 | // ------------------------- 34 | 35 | .hidden { 36 | display: none; 37 | visibility: hidden; 38 | } 39 | 40 | .hidden-mobile { 41 | @include bp(max-width medium) { 42 | display: none; 43 | visibility: hidden; 44 | } 45 | } 46 | 47 | .hidden-desktop { 48 | @include bp(medium) { 49 | display: none; 50 | visibility: hidden; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/styles/util/_whitespace.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Whitespace 3 | // ---------------------------------------------------------------------------- 4 | 5 | /** 6 | This is to help create a simple system for whitespace for blocks. It needs a 7 | single variable: $whitespace 8 | Shorthand Description: 9 | m Margin 10 | p Padding 11 | t Top 12 | r Right 13 | b Bottom 14 | l Left 15 | x X-axis (left and right) 16 | y Y-axis (top and bottom) 17 | 1 Whitespace Unit * 1 18 | 2 Whitespace Unit * 2 19 | etc 20 | EX: (assuming $whitespace = 12px) 21 | .mt4 == margin-top: 48px; 22 | .pl1 == padding-left: 12px; 23 | .py2 == padding-top: 24; padding-bottom: 24px; 24 | */ 25 | 26 | $whitespace: $unit; 27 | 28 | @for $i from 0 through 12 { 29 | .m#{$i} { 30 | margin: $whitespace * $i; 31 | } 32 | 33 | .mt#{$i} { 34 | margin-top: $whitespace * $i; 35 | } 36 | 37 | .mr#{$i} { 38 | margin-right: $whitespace * $i; 39 | } 40 | 41 | .mb#{$i} { 42 | margin-bottom: $whitespace * $i; 43 | } 44 | 45 | .ml#{$i} { 46 | margin-left: $whitespace * $i; 47 | } 48 | 49 | .my#{$i} { 50 | margin-top: $whitespace * $i; 51 | margin-bottom: $whitespace * $i; 52 | } 53 | 54 | .mx#{$i} { 55 | margin-left: $whitespace * $i; 56 | margin-right: $whitespace * $i; 57 | } 58 | } 59 | 60 | 61 | @for $i from 0 through 12 { 62 | .p#{$i} { 63 | padding: $whitespace * $i; 64 | } 65 | 66 | .pt#{$i} { 67 | padding-top: $whitespace * $i; 68 | } 69 | 70 | .pr#{$i} { 71 | padding-right: $whitespace * $i; 72 | } 73 | 74 | .pb#{$i} { 75 | padding-bottom: $whitespace * $i; 76 | } 77 | 78 | .pl#{$i} { 79 | padding-left: $whitespace * $i; 80 | } 81 | 82 | .py#{$i} { 83 | padding-top: $whitespace * $i; 84 | padding-bottom: $whitespace * $i; 85 | } 86 | 87 | .px#{$i} { 88 | padding-left: $whitespace * $i; 89 | padding-right: $whitespace * $i; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/styles/util/functions/_convert.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // REM Helpers 3 | // ---------------------------------------------------------------------------- 4 | 5 | // Remove the unit of a length 6 | // ---------------------------- 7 | // @param {Number} $number - Number to remove unit from 8 | // @return {Number} - Unitless number 9 | 10 | @function strip-unit($number) { 11 | @if type-of($number) == 'number' and not unitless($number) { 12 | @return $number / ($number * 0 + 1); 13 | } 14 | 15 | @return $number; 16 | } 17 | 18 | 19 | // Calculate REM 20 | // Takes a pixel unit and returns a REM unit 21 | @function calc-rem($size) { 22 | $rem-size: $size / $base-font-size; 23 | 24 | $rem-size-strip: strip-unit($rem-size); 25 | @return #{$rem-size-strip}rem; 26 | } 27 | 28 | // Calculate REM Fallback 29 | // Takes a pixel unit and returns a REM unit with px fallback 30 | @function calc-rem-fallback($size) { 31 | $rem-size: $size / $base-font-size; 32 | 33 | $rem-size-strip: strip-unit($rem-size); 34 | @return #{$rem-size-strip}rem; 35 | } 36 | 37 | 38 | // PX to REM or REM to PX (with PX fallback) 39 | // http://hugogiraudel.com/2013/03/18/ultimate-rem-mixin/ 40 | // Consider PostCSS in future 41 | // Usage: 42 | // 43 | // .class { 44 | // @include rem(font-size, 1.6rem); 45 | // @include rem(padding, 20px 10px); 46 | // } 47 | // 48 | // Output: 49 | // 50 | // .class { 51 | // font-size: 16px; /* Fallback for IE8 */ 52 | // font-size: 1.6rem; 53 | // padding: 20px 10px; /* Fallback for IE8 */ 54 | // padding: 2rem 1rem; 55 | // } 56 | 57 | @mixin rem($property, $values) { 58 | $px : (); 59 | $rem: (); 60 | 61 | @each $value in $values { 62 | 63 | @if $value == 0 or $value == auto { 64 | $px : append($px , $value); 65 | $rem: append($rem, $value); 66 | } 67 | 68 | @else { 69 | $unit: unit($value); 70 | $val: strip-unit($value); 71 | 72 | @if $unit == 'px' { 73 | $px : append($px, $value); 74 | $rem: append($rem, ($val / $base-font-size + rem)); 75 | } 76 | 77 | @if $unit == 'rem' { 78 | $px : append($px, ($val * $base-font-size + px)); 79 | $rem: append($rem, $value); 80 | } 81 | } 82 | } 83 | 84 | @if $px == $rem { 85 | #{$property}: $px; 86 | } @else { 87 | #{$property}: $px; 88 | #{$property}: $rem; 89 | } 90 | } 91 | 92 | 93 | // A big 'ol conversion function 94 | // ----------------------------- 95 | // $value: a number (without any unit) 96 | // $current-unit: the unit of the value to convert 97 | // $convert-unit: what unit to convert to. 98 | // Last 2 params accept px, em, rem & px 99 | 100 | @function convert($value-raw, $current-unit, $convert-unit) { 101 | $value: strip-unit($value-raw); 102 | @if $current-unit == px{ 103 | 104 | @if $convert-unit == em{ 105 | @return $value / $base-font-size + em; 106 | } 107 | 108 | @if $convert-unit == rem{ 109 | @return $value / $base-font-size + rem; 110 | } 111 | 112 | @else if $convert-unit == percent{ 113 | @return percentage($value / $base-font-size); 114 | } 115 | 116 | } 117 | 118 | @else if $current-unit == em{ 119 | 120 | @if $convert-unit == px{ 121 | @return $value * $base-font-size + px; 122 | } 123 | 124 | @else if $convert-unit == percent{ 125 | @return percentage($value); 126 | } 127 | 128 | } 129 | 130 | @else if $current-unit == percent{ 131 | 132 | @if $convert-unit == px{ 133 | @return $value * $base-font-size / 100 + px; 134 | } 135 | @else if $convert-unit == em{ 136 | @return $value / 100 + em; 137 | } 138 | @else if $convert-unit == rem{ 139 | @return $value / 100 + rem; 140 | } 141 | 142 | } 143 | 144 | @else if $current-unit == pts{ 145 | 146 | @if $convert-unit == px{ 147 | @return $value * 1.3333 + px; 148 | } 149 | @else if $convert-unit == em{ 150 | @return $value / 12 + em; 151 | } 152 | @else if $convert-unit == rem{ 153 | @return $value / 12 + rem; 154 | } 155 | @else if $convert-unit == percent{ 156 | @return percentage($value / 12) 157 | } 158 | } 159 | } 160 | 161 | // Helper function for the number function 162 | @function _length($number, $unit) { 163 | $strings: 'px' 'cm' 'mm' '%' 'ch' 'pica' 'in' 'em' 'rem' 'pt' 'pc' 'ex' 'vw' 'vh' 'vmin' 'vmax'; 164 | $units: 1px 1cm 1mm 1% 1ch 1pica 1in 1em 1rem 1pt 1pc 1ex 1vw 1vh 1vmin 1vmax; 165 | $index: index($strings, $unit); 166 | 167 | @if not $index { 168 | @warn "Unknown unit `#{$unit}`."; 169 | @return false; 170 | } 171 | 172 | @return $number * nth($units, $index); 173 | } 174 | 175 | // Converts a string to a number 176 | @function number($string) { 177 | // Matrices 178 | $strings: '0' '1' '2' '3' '4' '5' '6' '7' '8' '9'; 179 | $numbers: 0 1 2 3 4 5 6 7 8 9; 180 | 181 | // Result 182 | $result: 0; 183 | $divider: 0; 184 | $minus: false; 185 | 186 | // Looping through all characters 187 | @for $i from 1 through str-length($string) { 188 | $character: str-slice($string, $i, $i); 189 | $index: index($strings, $character); 190 | 191 | 192 | @if $character == '-' { 193 | $minus: true; 194 | } 195 | 196 | @else if $character == '.' { 197 | $divider: 1; 198 | } 199 | 200 | @else { 201 | @if not $index { 202 | $result: if($minus, $result * -1, $result); 203 | @return _length($result, str-slice($string, $i)); 204 | } 205 | 206 | $number: nth($numbers, $index); 207 | 208 | @if $divider == 0 { 209 | $result: $result * 10; 210 | } 211 | 212 | @else { 213 | // Move the decimal dot to the left 214 | $divider: $divider * 10; 215 | $number: $number / $divider; 216 | } 217 | 218 | $result: $result + $number; 219 | } 220 | } 221 | 222 | @return if($minus, $result * -1, $result); 223 | } 224 | -------------------------------------------------------------------------------- /src/styles/util/functions/_math.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Math Helpers 3 | // ---------------------------------------------------------------------------- 4 | 5 | // Returns the power based on a number and an exponent 6 | // --------------------------------------------------- 7 | 8 | @function pow($base, $exponent) { 9 | // reset value 10 | $value: $base; 11 | // positive intergers get multiplied 12 | @if $exponent > 1 { 13 | @for $i from 2 through $exponent { 14 | $value: $value * $base; } } 15 | // negitive intergers get divided. A number divided by itself is 1 16 | @if $exponent < 1 { 17 | @for $i from 0 through -$exponent { 18 | $value: $value / $base; } } 19 | // return the last value written 20 | @return $value; 21 | } 22 | -------------------------------------------------------------------------------- /src/styles/util/functions/_spacing.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Whitespace 3 | // ---------------------------------------------------------------------------- 4 | 5 | /** 6 | This is used to calculate spacing and help maintain vertical rhythm. 7 | Takes a factor, multiplies it with the current settings base spacing to output values that are multiples or dividends of the current vertical rhythm 8 | @param {Number} $factor [1] A factor with which to multiply the base-spacing value 9 | @return {Value} Value of base-spacing multiplied by the factor provided 10 | */ 11 | 12 | @function spacing($factor: 1) { 13 | @return $unit * $factor; 14 | } 15 | 16 | @function space($factor: 1) { 17 | @return $unit * $factor; 18 | } 19 | -------------------------------------------------------------------------------- /src/styles/util/mixins/_buttons.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Buttons Mixin 3 | // ---------------------------------------------------------------------------- 4 | /** 5 | Easily pump out default styles, as well as :hover, :focus, :active, 6 | and disabled options for all buttons 7 | Modified from Bootstrap 8 | */ 9 | 10 | 11 | /** 12 | Primary Button Mixin 13 | Takes a few variables and creates a new button style 14 | @param {color}: color of button text 15 | @param {color-hover} color of button text on hover 16 | @param {background}: color of button background 17 | @param {background-hover} color of button on hover 18 | @param {border} color of button border (defaults to background color) 19 | @param {border-hover} color of border on hover (defaults to hover bg color) 20 | */ 21 | 22 | @mixin button-variant($color, $color-hover, $background, $background-hover, $border: $background, $border-hover: $background-hover) { 23 | $active-background: $background-hover; 24 | $active-border: $border-hover; 25 | 26 | color: $color; 27 | background-color: $background; 28 | border-color: $border; 29 | 30 | &:hover { 31 | color: $color-hover; 32 | background-color: $active-background; 33 | border-color: $active-border; 34 | } 35 | 36 | &:focus { 37 | color: $color-hover; 38 | background-color: $active-background; 39 | border-color: $active-border; 40 | } 41 | 42 | &:active { 43 | color: $color-hover; 44 | background-color: $active-background; 45 | border-color: $active-border; 46 | } 47 | 48 | } 49 | 50 | /** 51 | Outline Button Mixin 52 | Takes a few variables and creates a new button style 53 | @param {color}: color of button text 54 | @param {border} color of button outline/border (default: $color) 55 | @param {background-hover}: color of background on hover (default: $color) 56 | @param {color-hover} color of text on hover (default: $white) 57 | @param {border-hover} color of border on hover (default: $background-hover) 58 | */ 59 | 60 | @mixin button-outline-variant($color, $border: $color, $background-hover: $color, $color-hover: $white, $border-hover: $background-hover) { 61 | color: $color; 62 | background-image: none; 63 | background-color: transparent; 64 | border-color: $border; 65 | 66 | &:hover { 67 | color: $color-hover; 68 | background-color: $background-hover; 69 | border-color: $border-hover; 70 | } 71 | 72 | &:focus { 73 | color: $color-hover; 74 | background-color: $background-hover; 75 | border-color: $border-hover; 76 | } 77 | 78 | &:active { 79 | color: $color-hover; 80 | background-color: $background-hover; 81 | border-color: $border-hover; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/styles/util/mixins/_clearfix.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Clearfix 3 | // ---------------------------------------------------------------------------- 4 | 5 | @mixin clearfix { 6 | &::after { 7 | clear: both; 8 | content: ''; 9 | display: table; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/styles/util/mixins/_type.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Type Mixins 3 | // ---------------------------------------------------------------------------- 4 | /** 5 | Takes an exponent, and returns an REM value based on a modulare scale with 6 | a px fallback. 7 | To learn more visit resources such as gridlover.net, modularscale.com, 8 | and type-scale.com. 9 | usage ex: font-size: type(3); 10 | return: font-size: 2.82715rem; 11 | Meant to be used as a mixin, but includes a function helper. 12 | */ 13 | 14 | @function type($exponent) { 15 | @return pow($scale, $exponent) * 1rem; 16 | } 17 | 18 | 19 | // This can be used to provide a px fallback. 20 | // Ex: @include typesize(3); 21 | // return: 22 | // font-size: 45.23px; 23 | // font-size: 2.82715rem; 24 | // scss-lint:disable DuplicateProperty 25 | 26 | @mixin typesize($exponent) { 27 | // scss-lint:disable DuplicateProperty 28 | font-size: $base-font-size * pow($scale, $exponent); 29 | font-size: type($exponent); 30 | // scss-lint:enable DuplicateProperty 31 | } 32 | -------------------------------------------------------------------------------- /src/styles/util/mixins/_z-index.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Z-Index 3 | // ---------------------------------------------------------------------------- 4 | 5 | /******************* 6 | This helps keep z-indexes from becoming a clusterf*ck. 7 | usage ex: 8 | .header { @include z-index(header) } 9 | meant to be used as a mixin, but can also use function: 10 | .header { z-index(header) } 11 | *******************/ 12 | 13 | @function z-index($key) { 14 | @return map-get($z-index, $key); 15 | } 16 | 17 | @mixin z-index($key) { 18 | z-index: z-index($key); 19 | } 20 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MozillaDevelopers/playground/2ff5d7727ee9e073514daaa8dd8b2b0f95725fe8/static/favicon.ico --------------------------------------------------------------------------------