├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs └── Slides.pdf ├── exerciseLICENSE ├── exercises ├── bem │ ├── exercise.json │ ├── public │ │ ├── index.html │ │ └── tests.js │ └── src │ │ └── sass │ │ └── app.scss ├── extend │ ├── exercise.json │ ├── public │ │ ├── index.html │ │ └── tests.js │ └── src │ │ └── sass │ │ └── app.scss ├── functions │ ├── exercise.json │ ├── public │ │ ├── index.html │ │ └── tests.js │ └── src │ │ └── sass │ │ ├── _mixins.scss │ │ ├── _variables.scss │ │ └── app.scss ├── if │ ├── exercise.json │ ├── public │ │ ├── index.html │ │ └── tests.js │ └── src │ │ └── sass │ │ ├── _mixins.scss │ │ ├── _variables.scss │ │ └── app.scss ├── luminance │ ├── exercise.json │ ├── public │ │ ├── index.html │ │ └── tests.js │ └── src │ │ └── sass │ │ ├── _functions.scss │ │ ├── _mixins.scss │ │ ├── _variables.scss │ │ └── app.scss ├── mixins │ ├── exercise.json │ ├── public │ │ ├── index.html │ │ └── tests.js │ └── src │ │ └── sass │ │ ├── _mixins.scss │ │ ├── _variables.scss │ │ └── app.scss ├── nesting │ ├── exercise.json │ ├── public │ │ ├── index.html │ │ └── tests.js │ └── src │ │ └── sass │ │ └── app.scss ├── parent │ ├── exercise.json │ ├── public │ │ ├── index.html │ │ └── tests.js │ └── src │ │ └── sass │ │ └── app.scss ├── range │ ├── exercise.json │ ├── public │ │ ├── index.html │ │ └── tests.js │ └── src │ │ └── sass │ │ ├── _mixins.scss │ │ ├── _variables.scss │ │ └── app.scss ├── tiny │ ├── exercise.json │ ├── public │ │ ├── index.html │ │ └── tests.js │ └── src │ │ └── sass │ │ └── app.scss └── variables │ ├── exercise.json │ ├── public │ ├── index.html │ └── tests.js │ └── src │ └── sass │ ├── _variables.scss │ └── app.scss ├── package.json ├── public ├── css │ └── normalize.css ├── index.html └── js │ ├── tester.js │ └── utils.js ├── renovate.json ├── run ├── run.bat ├── src ├── exercise.js ├── program.js └── server.js ├── tools └── rebase-exercise-branches.js └── yarn.lock /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Something broke while you were progressing through the workshop 4 | 5 | --- 6 | 7 | 8 | 11 | 12 | - [ ] **System Information** 13 | - [ ] Browser type and version 14 | - [ ] OS type and version 15 | - [ ] WINDOWS: be sure to indicate which terminal you're using -- (i.e., cmd.exe, powershell, git- bash, cygwin, Ubuntu via windows subsystem for linux, etc...) 16 | - [ ] Node version 17 | - [ ] Any error messages that may be in the console where you ran npm start 18 | - [ ] Any error messages in the JS console 19 | 20 | - [ ] **Describe the bug** 21 | 22 | 23 | - [ ] **To Reproduce** 24 | Steps to reproduce the behavior: 25 | 1. Go to '...' 26 | 2. Click on '....' 27 | 3. Scroll down to '....' 28 | 4. See error 29 | 30 | - [ ] **Expected behavior** 31 | A clear and concise description of what you expected to happen. 32 | 33 | - [ ] **Screenshots (optional)** 34 | If applicable, add screenshots to help explain your problem. 35 | 36 | - [ ] **Additional context (optional)** 37 | Add any other context about the problem here. 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | exercises/**/public/*.css -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | - "8.17.0" 5 | 6 | sudo: required 7 | dist: trusty 8 | 9 | addons: 10 | chrome: stable 11 | 12 | cache: 13 | directories: 14 | - $HOME/.npm 15 | before_install: 16 | - npm config set spin false 17 | 18 | before_script: 19 | - sudo chmod 4755 /opt/google/chrome/chrome-sandbox 20 | - sudo chown root /opt/google/chrome/chrome-sandbox 21 | 22 | install: 23 | - yarn install --non-interactive 24 | 25 | script: npm run test:ci 26 | 27 | after_success: 28 | - npm run travis-deploy-once "npm run semantic-release" 29 | branches: 30 | except: 31 | - /^v\d+\.\d+\.\d+$/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.0.13](https://github.com/mike-works/sass-fundamentals.git/compare/v1.0.12...v1.0.13) (2020-02-05) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * **deps:** update dependency livereload to v0.9.1 ([07d22e8](https://github.com/mike-works/sass-fundamentals.git/commit/07d22e8)) 7 | 8 | ## [1.0.12](https://github.com/mike-works/sass-fundamentals.git/compare/v1.0.11...v1.0.12) (2020-01-30) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * **deps:** update dependency livereload to v0.9.0 ([8325072](https://github.com/mike-works/sass-fundamentals.git/commit/8325072)) 14 | 15 | ## [1.0.11](https://github.com/mike-works/sass-fundamentals.git/compare/v1.0.10...v1.0.11) (2019-10-11) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * **deps:** update dependency commander to v2.20.3 ([42a0487](https://github.com/mike-works/sass-fundamentals.git/commit/42a0487)) 21 | 22 | ## [1.0.10](https://github.com/mike-works/sass-fundamentals.git/compare/v1.0.9...v1.0.10) (2019-10-03) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * **deps:** update dependency livereload to v0.8.2 ([5d0cd67](https://github.com/mike-works/sass-fundamentals.git/commit/5d0cd67)) 28 | 29 | ## [1.0.9](https://github.com/mike-works/sass-fundamentals.git/compare/v1.0.8...v1.0.9) (2019-09-28) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * **deps:** update dependency commander to v2.20.1 ([eda81bc](https://github.com/mike-works/sass-fundamentals.git/commit/eda81bc)) 35 | 36 | ## [1.0.8](https://github.com/mike-works/sass-fundamentals.git/compare/v1.0.7...v1.0.8) (2019-09-17) 37 | 38 | 39 | ### Bug Fixes 40 | 41 | * **deps:** update dependency livereload to v0.8.1 ([836ccc0](https://github.com/mike-works/sass-fundamentals.git/commit/836ccc0)) 42 | 43 | ## [1.0.7](https://github.com/mike-works/sass-fundamentals.git/compare/v1.0.6...v1.0.7) (2019-05-26) 44 | 45 | 46 | ### Bug Fixes 47 | 48 | * **deps:** update dependency express to v4.17.1 ([65624c5](https://github.com/mike-works/sass-fundamentals.git/commit/65624c5)) 49 | 50 | ## [1.0.6](https://github.com/mike-works/sass-fundamentals.git/compare/v1.0.5...v1.0.6) (2019-05-25) 51 | 52 | 53 | ### Bug Fixes 54 | 55 | * **deps:** update dependency livereload to v0.8.0 ([d508aa1](https://github.com/mike-works/sass-fundamentals.git/commit/d508aa1)) 56 | 57 | ## [1.0.5](https://github.com/mike-works/sass-fundamentals.git/compare/v1.0.4...v1.0.5) (2019-05-17) 58 | 59 | 60 | ### Bug Fixes 61 | 62 | * **deps:** update dependency express to v4.17.0 ([01a8f78](https://github.com/mike-works/sass-fundamentals.git/commit/01a8f78)) 63 | 64 | ## [1.0.4](https://github.com/mike-works/sass-fundamentals.git/compare/v1.0.3...v1.0.4) (2019-04-03) 65 | 66 | 67 | ### Bug Fixes 68 | 69 | * **deps:** update dependency commander to v2.20.0 ([b19c882](https://github.com/mike-works/sass-fundamentals.git/commit/b19c882)) 70 | 71 | ## [1.0.3](https://github.com/mike-works/sass-fundamentals.git/compare/v1.0.2...v1.0.3) (2019-01-05) 72 | 73 | 74 | ### Bug Fixes 75 | 76 | * **deps:** update dependency chalk to v2.4.2 ([2816bc0](https://github.com/mike-works/sass-fundamentals.git/commit/2816bc0)) 77 | 78 | ## [1.0.2](https://github.com/mike-works/sass-fundamentals.git/compare/v1.0.1...v1.0.2) (2018-11-13) 79 | 80 | 81 | ### Bug Fixes 82 | 83 | * shared renovate and semantic-release config ([d2713f2](https://github.com/mike-works/sass-fundamentals.git/commit/d2713f2)) 84 | 85 | ## [1.0.1](https://github.com/mike-works/sass-fundamentals/compare/v1.0.0...v1.0.1) (2018-08-21) 86 | 87 | 88 | ### Bug Fixes 89 | 90 | * ci-badge ([b0d2ec6](https://github.com/mike-works/sass-fundamentals/commit/b0d2ec6)) 91 | * enable custom semantic-release config ([3c8ac3b](https://github.com/mike-works/sass-fundamentals/commit/3c8ac3b)) 92 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Mike Works, Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 | This is the project used for the Mike.Works Sass Fundamentals course. 22 |
23 | 24 | # Course outline and slides 25 | * [View course outline here](https://mike.works/course/sass-fundamentals-5438fec/stage/sass-fundamentals-ca61dca) 26 | * [View slides here](https://docs.mike.works/sass-fundamentals) 27 | 28 | # What are the pieces? 29 | 30 | * [node-sass-middleware](https://github.com/sass/node-sass-middleware) for Sass compilation 31 | * [express](http://expressjs.com/) for serving HTML and CSS (compiled from Sass) 32 | * [commander](https://github.com/tj/commander.js) as a foundation for a CLI to run exerises 33 | * [A little CSS testing framework](https://github.com/mike-works/sass-fundamentals/blob/master/public/js/tester.js) for asserting that exercise goals have been reached! 34 | 35 | # Getting Set Up 36 | 37 | There are a few things you need to ensure you have installed, in order to be ready for this course. 38 | 39 | ### Node.js 40 | 41 | You’ll need a relatively recent version (v4.5 or newer, v7 ideally) of node.js installed. On OS X, a great way of doing this without disturbing your existing dev environment is to install NVM. [Installation instructions are here](https://github.com/creationix/nvm#installation). 42 | 43 | You’ll know everything is set up properly when you can run 44 | 45 | ``` 46 | nvm --version # might look like "0.31.4" 47 | node --version # might look like "v7.7.3" 48 | ``` 49 | 50 | ### Visual Studio Code 51 | 52 | Particularly if you’ve never tried it before, you should install [Microsoft Visual Studio Code](https://code.visualstudio.com/). Some fantastic extensions that I use regularly include 53 | * [css-triggers](https://marketplace.visualstudio.com/items?itemName=kisstkondoros.csstriggers) 54 | * [vscode-icons](https://marketplace.visualstudio.com/items?itemName=robertohuertasm.vscode-icons) 55 | * [Sublime Text Keymap](https://marketplace.visualstudio.com/items?itemName=ms-vscode.sublime-keybindings) - Install if you’re used to sublime text keyboard shortcuts 56 | 57 | ### Check out and setup the project for this workshop 58 | 59 | ``` 60 | git clone git@github.com:mike-works/sass-fundamentals.git 61 | cd sass-fundamentals 62 | npm install 63 | ``` 64 | 65 | 66 | # How to use it 67 | Use the `run` command to launch an exercise 68 | 69 | ```sh 70 | ./run --exercise9 | 12 | 13 |
14 | 15 |16 | 19 | 20 |
21 | 22 |23 | 26 | 27 |
28 | 29 |30 | 33 | 34 |
35 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /exercises/bem/public/tests.js: -------------------------------------------------------------------------------- 1 | test('Primary button', function(assert) { 2 | assert.hasStyle('.btn.btn--mode-primary', { 3 | 'padding-left': '10px', 4 | 'padding-right': '10px', 5 | 'padding-top': '2px', 6 | 'padding-bottom': '2px', 7 | 'line-height': '20px', 8 | 'border-radius': assert.compare.eq('2px'), 9 | 'border-style': 'solid', 10 | 'border-color': assert.compare.truthy(), 11 | 'border-width': assert.compare.gt(0) 12 | }, 'styles are incorrect'); 13 | 14 | assert.hasStyle('.btn.btn--mode-primary', { 15 | 'background-color': 'rgb(204, 68, 102)', 16 | 'color': 'rgb(255, 255, 255)', 17 | 'opacity': '1' 18 | }, 'colors for enabled state are incorrect'); 19 | assert.hasStyle('.btn.btn--mode-primary:disabled', { 20 | 'opacity': '0.5' 21 | }, 'colors for enabled state are incorrect'); 22 | 23 | }); 24 | 25 | test('Secondary button', function(assert) { 26 | assert.hasStyle('.btn.btn--mode-secondary', { 27 | 'padding-left': '10px', 28 | 'padding-right': '10px', 29 | 'padding-top': '2px', 30 | 'padding-bottom': '2px', 31 | }, 'padding is incorrect'); 32 | 33 | assert.hasStyle('.btn.btn--mode-secondary', { 34 | 'background-color': 'rgb(237, 188, 200)', 35 | 'color': 'rgb(0, 0, 0)', 36 | 'opacity': '1' 37 | }, 'colors for enabled state are incorrect'); 38 | assert.hasStyle('.btn.btn--mode-secondary:disabled', { 39 | 'opacity': '0.5' 40 | }, 'colors for enabled state are incorrect'); 41 | }); 42 | 43 | test('Price', function(assert) { 44 | assert.hasStyle('.btn .btn__price', { 45 | 'padding-left': '3px', 46 | 'padding-right': '3px', 47 | 'padding-top': '1px', 48 | 'padding-bottom': '1px', 49 | }, 'padding is incorrect'); 50 | 51 | assert.hasStyle('.btn .btn__price', { 52 | 'background-color': '#008000', 53 | 'color': '#ffffff' 54 | }, 'colors for enabled state are incorrect'); 55 | assert.hasStyle('.btn:disabled .btn__price', { 56 | 'background-color': '#aaaaaa', 57 | 'color': '#ffffff' 58 | }, 'colors for enabled state are incorrect'); 59 | }); 60 | -------------------------------------------------------------------------------- /exercises/bem/src/sass/app.scss: -------------------------------------------------------------------------------- 1 | body { 2 | h1 { 3 | color: #c69; 4 | } 5 | } 6 | 7 | .btn { 8 | padding: 2px 10px; 9 | border-style: solid; 10 | border-width: 1px; 11 | line-height: 20px; 12 | border-radius: 2px; 13 | } -------------------------------------------------------------------------------- /exercises/extend/exercise.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Extend", 3 | "instructions": "Use the @extend directive to build buttons" 4 | } -------------------------------------------------------------------------------- /exercises/extend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |9 | 10 | 11 |
12 | 13 |14 | 15 | 16 |
17 | 18 |19 | 20 | 21 |
22 | 23 |24 | 25 | 26 |
27 |28 | 29 | 30 | 31 |
32 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /exercises/extend/public/tests.js: -------------------------------------------------------------------------------- 1 | test('Primary button', function(assert) { 2 | assert.hasStyle('.btn.btn-primary', { 3 | 'padding-left': '10px', 4 | 'padding-right': '10px', 5 | 'padding-top': '2px', 6 | 'padding-bottom': '2px', 7 | 'border-radius': assert.compare.eq('2px'), 8 | 'border-style': 'solid', 9 | 'border-color': assert.compare.truthy(), 10 | 'border-width': assert.compare.gt(0) 11 | }, 'styles are incorrect'); 12 | 13 | assert.hasStyle('.btn.btn-primary', { 14 | 'background-color': 'rgb(204, 68, 102)', 15 | 'color': 'rgb(255, 255, 255)', 16 | 'opacity': '1' 17 | }, 'colors for enabled state are incorrect'); 18 | assert.hasStyle('.btn.btn-primary:disabled', { 19 | 'opacity': '0.5' 20 | }, 'colors for enabled state are incorrect'); 21 | 22 | }); 23 | 24 | test('Secondary button', function(assert) { 25 | assert.hasStyle('.btn.btn-secondary', { 26 | 'padding-left': '10px', 27 | 'padding-right': '10px', 28 | 'padding-top': '2px', 29 | 'padding-bottom': '2px', 30 | }, 'padding is incorrect'); 31 | 32 | assert.hasStyle('.btn.btn-secondary', { 33 | 'background-color': 'rgb(237, 188, 200)', 34 | 'color': 'rgb(0, 0, 0)', 35 | 'opacity': '1' 36 | }, 'colors for enabled state are incorrect'); 37 | assert.hasStyle('.btn.btn-secondary:disabled', { 38 | 'opacity': '0.5' 39 | }, 'colors for enabled state are incorrect'); 40 | }); 41 | 42 | test('Unstyled button', function(assert) { 43 | $unstyled = document.querySelector('button.no-style'); 44 | $unstyledCss = window.getComputedStyle($unstyled); 45 | 46 | assert.hasStyle('.unstyled .btn', { 47 | 'padding-left': $unstyledCss['padding-left'], 48 | 'padding-right': $unstyledCss['padding-right'], 49 | 'padding-top': $unstyledCss['padding-top'], 50 | 'padding-bottom': $unstyledCss['padding-bottom'], 51 | }, 'padding is incorrect'); 52 | 53 | assert.hasStyle('.unstyled .btn', { 54 | 'background-color': $unstyledCss['background-color'], 55 | 'color': $unstyledCss['color'], 56 | 'opacity': $unstyledCss['opacity'] 57 | }, 'colors for enabled state are incorrect'); 58 | assert.hasStyle('.unstyled .btn:disabled', { 59 | 'opacity': $unstyledCss['opacity'] 60 | }, 'colors for enabled state are incorrect'); 61 | }); 62 | -------------------------------------------------------------------------------- /exercises/extend/src/sass/app.scss: -------------------------------------------------------------------------------- 1 | body { 2 | h1 { 3 | color: #c69; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /exercises/functions/exercise.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Functions - Color", 3 | "instructions": "Use color functions with a mixin to generate multiple themes for our set of buttons" 4 | } -------------------------------------------------------------------------------- /exercises/functions/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |10 | Theme 1 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |20 | Theme 2 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |30 | Theme 3 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /exercises/functions/public/tests.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | function testTheme(themename, c1, c2, c3, c4) { 3 | test(themename + ' button colors', function(assert) { 4 | assert.hasStyle('.' + themename + ' .btn.btn-primary', { 5 | 'background-color': c1 6 | }, 'primary button bg color is incorrect'); 7 | assert.hasStyle('.' + themename + ' .btn.btn-secondary', { 8 | 'background-color': c2 9 | }, 'secondary button bg color is incorrect'); 10 | assert.hasStyle('.' + themename + ' .btn.btn-other', { 11 | 'background-color': c3 12 | }, 'other button bg color is incorrect'); 13 | assert.hasStyle('.' + themename + ' .btn.btn-other2', { 14 | 'background-color': c4 15 | }, 'other2 button bg color is incorrect'); 16 | }); 17 | } 18 | test('First button', function(assert) { 19 | assert.hasStyle('.btn', { 20 | 'border-radius': assert.compare.eq('2px'), 21 | 'border-style': 'solid', 22 | 'border-color': assert.compare.truthy(), 23 | 'border-width': assert.compare.gt(0), 24 | 'display': 'inline-block' 25 | }, 'styles are incorrect'); 26 | }) 27 | 28 | testTheme('theme-1', '#339999', '#3c4d1a', '#3c1a4d', '#4d1a1a'); 29 | testTheme('theme-2', '#660099', '#006644', '#664400', '#226600'); 30 | testTheme('theme-3', '#cc6699', '#336699', '#669933', '#339966'); 31 | }()); -------------------------------------------------------------------------------- /exercises/functions/src/sass/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin theme($primary-color, $rotate: 120deg, $darkenpct: 20%) { 2 | 3 | } -------------------------------------------------------------------------------- /exercises/functions/src/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | $hopbush: #c69 !default; 2 | $patina: #699 !default; 3 | $venus: #998099 !default; 4 | $nebula: #d2e1dd !default; 5 | 6 | $black: #000 !default; 7 | $white: #fff !default; 8 | 9 | $btn-disabled-opacity: 0.5 !default; 10 | 11 | -------------------------------------------------------------------------------- /exercises/functions/src/sass/app.scss: -------------------------------------------------------------------------------- 1 | @import '_variables'; 2 | @import '_mixins'; 3 | 4 | .btn { 5 | padding: 2px 10px; 6 | border: 1px solid transparent; 7 | border-radius: 2px; 8 | color: #fff; 9 | &:hover:not(:disabled) { 10 | box-shadow: 0 5px 15px rgba(0, 0, 0, 0.8); 11 | } 12 | &:disabled { 13 | color: #666; 14 | opacity: 0.5; 15 | } 16 | } 17 | 18 | .theme-1 { 19 | @include theme(#399, 100deg); 20 | } 21 | 22 | .theme-2 { 23 | @include theme(#609, $darkenpct: 10%); 24 | } 25 | 26 | .theme-3 { 27 | @include theme($hopbush); 28 | } -------------------------------------------------------------------------------- /exercises/if/exercise.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Control Flow - @if", 3 | "instructions": "Use @if to set button text color to either black or white, on a per-button basis" 4 | } -------------------------------------------------------------------------------- /exercises/if/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |10 | Theme 1 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |20 | Theme 2 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |30 | Theme 3 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /exercises/if/public/tests.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | function rgbToHsl(r, g, b) { 3 | r /= 255, g /= 255, b /= 255; 4 | 5 | var max = Math.max(r, g, b), min = Math.min(r, g, b); 6 | var h, s, l = (max + min) / 2; 7 | 8 | if (max == min) { 9 | h = s = 0; // achromatic 10 | } else { 11 | var d = max - min; 12 | s = l > 0.5 ? d / (2 - max - min) : d / (max + min); 13 | 14 | switch (max) { 15 | case r: h = (g - b) / d + (g < b ? 6 : 0); break; 16 | case g: h = (b - r) / d + 2; break; 17 | case b: h = (r - g) / d + 4; break; 18 | } 19 | 20 | h /= 6; 21 | } 22 | 23 | return [h, s, l]; 24 | } 25 | 26 | function whiteIfAbove70Brightness(h) { 27 | var hex = h.replace('#', ''); 28 | var r = parseInt(hex.substring(0, 2), 16); 29 | var g = parseInt(hex.substring(2, 4), 16); 30 | var b = parseInt(hex.substring(4, 6), 16); 31 | var parts = rgbToHsl(r, g, b); 32 | var brightness = parts[2] 33 | return brightness >= 0.7 ? '#000000' : '#ffffff'; 34 | } 35 | 36 | function testTheme(themename, c1, c2, c3, c4) { 37 | test(themename + ' button colors', function (assert) { 38 | assert.hasStyle('.' + themename + ' .btn.btn-primary', { 39 | 'background-color': c1, 40 | color: whiteIfAbove70Brightness(c1) 41 | }, 'primary button styles are incorrect'); 42 | assert.hasStyle('.' + themename + ' .btn.btn-secondary', { 43 | 'background-color': c2, 44 | color: whiteIfAbove70Brightness(c2) 45 | }, 'secondary button styles are incorrect'); 46 | assert.hasStyle('.' + themename + ' .btn.btn-other', { 47 | 'background-color': c3, 48 | color: whiteIfAbove70Brightness(c3) 49 | }, 'other button styles are incorrect'); 50 | assert.hasStyle('.' + themename + ' .btn.btn-other2', { 51 | 'background-color': c4, 52 | color: whiteIfAbove70Brightness(c4) 53 | }, 'other2 button styles are incorrect'); 54 | }); 55 | } 56 | test('First button', function (assert) { 57 | assert.hasStyle('.btn', { 58 | 'border-radius': assert.compare.eq('2px'), 59 | 'border-style': 'solid', 60 | 'border-color': assert.compare.truthy(), 61 | 'border-width': assert.compare.gt(0), 62 | 'display': 'inline-block' 63 | }, 'styles are incorrect'); 64 | }) 65 | 66 | testTheme('theme-1', '#99ff99', '#3377ff', '#ff7733', '#ff33ff'); 67 | testTheme('theme-2', '#9999ff', '#ff6666', '#66ff66', '#ffff66'); 68 | testTheme('theme-3', '#aa66aa', '#6d6d3d', '#3d6d6d', '#3d6d3d'); 69 | }()); -------------------------------------------------------------------------------- /exercises/if/src/sass/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin theme($primary-color, $rotate: 120deg, $darkenpct: 20%) { 2 | .btn-primary { 3 | @include button-base($primary-color); 4 | } 5 | .btn-secondary { 6 | @include button-base( 7 | darken( 8 | adjust-hue($primary-color, $rotate), 9 | $darkenpct) 10 | ); 11 | } 12 | .btn-other { 13 | @include button-base( 14 | darken( 15 | adjust-hue($primary-color, 360deg - $rotate), 16 | $darkenpct) 17 | ); 18 | } 19 | .btn-other2 { 20 | @include button-base( 21 | darken( 22 | adjust-hue($primary-color, 180deg), 23 | $darkenpct) 24 | ); 25 | } 26 | } 27 | 28 | @mixin button-base($color) { 29 | background-color: $color; 30 | border-color: darken($color, 20%); 31 | &:hover:not(:disabled) { 32 | background-color: lighten(saturate($color, 20%), 10%); 33 | } 34 | } -------------------------------------------------------------------------------- /exercises/if/src/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | $hopbush: #c69 !default; 2 | $patina: #699 !default; 3 | $venus: #998099 !default; 4 | $nebula: #d2e1dd !default; 5 | 6 | $black: #000 !default; 7 | $white: #fff !default; 8 | 9 | $btn-disabled-opacity: 0.5 !default; 10 | 11 | -------------------------------------------------------------------------------- /exercises/if/src/sass/app.scss: -------------------------------------------------------------------------------- 1 | @import '_variables'; 2 | @import '_mixins'; 3 | 4 | .btn { 5 | padding: 2px 10px; 6 | border: 1px solid transparent; 7 | border-radius: 2px; 8 | &:hover:not(:disabled) { 9 | box-shadow: 0 5px 15px rgba(0, 0, 0, 0.8); 10 | } 11 | &:disabled { 12 | color: #666; 13 | opacity: 0.5; 14 | } 15 | } 16 | 17 | .theme-1 { 18 | @include theme(#9f9, 100deg); 19 | } 20 | 21 | .theme-2 { 22 | @include theme(#99f, $darkenpct: 10%); 23 | } 24 | 25 | .theme-3 { 26 | @include theme(#a6a); 27 | } 28 | -------------------------------------------------------------------------------- /exercises/luminance/exercise.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Writing custom functions", 3 | "instructions": "Take this solution to a previous exercise, and update it to base the logic for black/white button text to use contrast instead of brightness" 4 | } -------------------------------------------------------------------------------- /exercises/luminance/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |10 | Theme 1 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |20 | Theme 2 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |30 | Theme 3 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /exercises/luminance/public/tests.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | function rgbToHsl(r, g, b) { 3 | r /= 255, g /= 255, b /= 255; 4 | 5 | var max = Math.max(r, g, b), min = Math.min(r, g, b); 6 | var h, s, l = (max + min) / 2; 7 | 8 | if (max == min) { 9 | h = s = 0; // achromatic 10 | } else { 11 | var d = max - min; 12 | s = l > 0.5 ? d / (2 - max - min) : d / (max + min); 13 | 14 | switch (max) { 15 | case r: h = (g - b) / d + (g < b ? 6 : 0); break; 16 | case g: h = (b - r) / d + 2; break; 17 | case b: h = (r - g) / d + 4; break; 18 | } 19 | 20 | h /= 6; 21 | } 22 | 23 | return [h, s, l]; 24 | } 25 | 26 | function testTheme(themename, c1, c2, c3, c4, c5, c6, c7, c8) { 27 | test(themename + ' button colors', function (assert) { 28 | assert.hasStyle('.' + themename + ' .btn.btn-primary', { 29 | 'background-color': c1, 30 | color: c5 31 | }, 'primary button styles are incorrect'); 32 | assert.hasStyle('.' + themename + ' .btn.btn-secondary', { 33 | 'background-color': c2, 34 | color: c6 35 | }, 'secondary button styles are incorrect'); 36 | assert.hasStyle('.' + themename + ' .btn.btn-other', { 37 | 'background-color': c3, 38 | color: c7 39 | }, 'other button styles are incorrect'); 40 | assert.hasStyle('.' + themename + ' .btn.btn-other2', { 41 | 'background-color': c4, 42 | color: c8 43 | }, 'other2 button styles are incorrect'); 44 | }); 45 | } 46 | test('First button', function (assert) { 47 | assert.hasStyle('.btn', { 48 | 'border-radius': assert.compare.eq('2px'), 49 | 'border-style': 'solid', 50 | 'border-color': assert.compare.truthy(), 51 | 'border-width': assert.compare.gt(0), 52 | 'display': 'inline-block' 53 | }, 'styles are incorrect'); 54 | }) 55 | 56 | testTheme('theme-1', '#99ff99', '#3377ff', '#ff7733', '#ff33ff', '#000000', '#ffffff', '#000000', '#000000'); 57 | testTheme('theme-2', '#9999ff', '#ff6666', '#66ff66', '#ffff66', '#000000', '#000000', '#000000', '#000000'); 58 | testTheme('theme-3', '#6666ff', '#ff0000', '#00ff00', '#ffff00', '#ffffff', '#ffffff', '#000000', '#000000'); 59 | }()); -------------------------------------------------------------------------------- /exercises/luminance/src/sass/_functions.scss: -------------------------------------------------------------------------------- 1 | /* Source: https://css-tricks.com/snippets/sass/power-function/ */ 2 | @function pow($number, $exponent) { 3 | @if (round($exponent) != $exponent) { 4 | @return exp($exponent * ln($number)); 5 | } 6 | 7 | $value: 1; 8 | 9 | @if $exponent > 0 { 10 | @for $i from 1 through $exponent { 11 | $value: $value * $number; 12 | } 13 | } @else if $exponent < 0 { 14 | @for $i from 1 through -$exponent { 15 | $value: $value / $number; 16 | } 17 | } 18 | 19 | @return $value; 20 | } 21 | 22 | @function factorial($value) { 23 | $result: 1; 24 | 25 | @if $value == 0 { 26 | @return $result; 27 | } 28 | 29 | @for $index from 1 through $value { 30 | $result: $result * $index; 31 | } 32 | 33 | @return $result; 34 | } 35 | 36 | @function summation($iteratee, $input, $initial: 0, $limit: 100) { 37 | $sum: 0; 38 | 39 | @for $index from $initial to $limit { 40 | $sum: $sum + call($iteratee, $input, $index); 41 | } 42 | 43 | @return $sum; 44 | } 45 | 46 | @function exp-maclaurin($x, $n) { 47 | @return (pow($x, $n) / factorial($n)); 48 | } 49 | 50 | @function exp($value) { 51 | @return summation('exp-maclaurin', $value, 0, 100); 52 | } 53 | 54 | @function ln-maclaurin($x, $n) { 55 | @return (pow(-1, $n + 1) / $n) * (pow($x - 1, $n)); 56 | } 57 | 58 | @function ln($value) { 59 | $ten-exp: 1; 60 | $ln-ten: 2.30258509; 61 | 62 | @while ($value > pow(10, $ten-exp)) { 63 | $ten-exp: $ten-exp + 1; 64 | } 65 | 66 | @return summation(ln-maclaurin, $value / pow(10, $ten-exp), 1, 100) + $ten-exp * $ln-ten; 67 | } -------------------------------------------------------------------------------- /exercises/luminance/src/sass/_mixins.scss: -------------------------------------------------------------------------------- 1 | @import "_functions"; 2 | 3 | @mixin theme($primary-color, $rotate: 120deg, $darkenpct: 20%) { 4 | .btn-primary { 5 | @include button-base($primary-color); 6 | } 7 | .btn-secondary { 8 | @include button-base( 9 | darken( 10 | adjust-hue($primary-color, $rotate), 11 | $darkenpct) 12 | ); 13 | } 14 | .btn-other { 15 | @include button-base( 16 | darken( 17 | adjust-hue($primary-color, 360deg - $rotate), 18 | $darkenpct) 19 | ); 20 | } 21 | .btn-other2 { 22 | @include button-base( 23 | darken( 24 | adjust-hue($primary-color, 180deg), 25 | $darkenpct) 26 | ); 27 | } 28 | } 29 | 30 | @function luminance($color) { 31 | /* REPLACE THIS WITH YOUR REAL FUNCTION */ 32 | @return 0.5; 33 | } 34 | 35 | $white_luminance: luminance(white); 36 | 37 | @mixin button-base($color) { 38 | background-color: $color; 39 | border-color: darken($color, 20%); 40 | /* luminance(#{$color}): #{luminance($color)} 41 | * luminance(white): #{luminance(white)} 42 | */ 43 | @if abs(luminance($color) - $white_luminance) > 0.7 { 44 | // @if lightness($color) < 70 { 45 | color: white; 46 | } 47 | &:hover:not(:disabled) { 48 | background-color: lighten(saturate($color, 20%), 10%); 49 | } 50 | } -------------------------------------------------------------------------------- /exercises/luminance/src/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | $hopbush: #c69 !default; 2 | $patina: #699 !default; 3 | $venus: #998099 !default; 4 | $nebula: #d2e1dd !default; 5 | 6 | $black: #000 !default; 7 | $white: #fff !default; 8 | 9 | $btn-disabled-opacity: 0.5 !default; 10 | 11 | -------------------------------------------------------------------------------- /exercises/luminance/src/sass/app.scss: -------------------------------------------------------------------------------- 1 | @import '_variables'; 2 | @import '_mixins'; 3 | 4 | .btn { 5 | padding: 2px 10px; 6 | border: 1px solid transparent; 7 | border-radius: 2px; 8 | &:hover:not(:disabled) { 9 | box-shadow: 0 5px 15px rgba(0, 0, 0, 0.8); 10 | } 11 | &:disabled { 12 | color: #666; 13 | opacity: 0.5; 14 | } 15 | } 16 | 17 | .theme-1 { 18 | @include theme(#9f9, 100deg); 19 | } 20 | 21 | .theme-2 { 22 | @include theme(#99f, $darkenpct: 10%); 23 | } 24 | 25 | .theme-3 { 26 | @include theme(#66f); 27 | } 28 | -------------------------------------------------------------------------------- /exercises/mixins/exercise.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Mixins", 3 | "instructions": "Use mixins to create a bunch of colorful buttons" 4 | } -------------------------------------------------------------------------------- /exercises/mixins/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |10 | 11 | 12 |
13 | 14 |15 | 16 | 17 |
18 |19 | 20 | 21 |
22 |23 | 24 | 25 |
26 |27 | 28 | 29 |
30 |31 | 32 | 33 |
34 |35 | 36 | 37 |
38 | 39 | 40 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /exercises/mixins/public/tests.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | function testButtonColor(colorName, fontColor, bgColor) { 3 | test(colorName + ' button', function(assert) { 4 | assert.hasStyle('.btn.btn-' + colorName, { 5 | color: fontColor, 6 | 'background-color': bgColor 7 | }, 'Colors are correct') 8 | }); 9 | } 10 | test('First button', function(assert) { 11 | assert.hasStyle('.btn', { 12 | 'border-radius': assert.compare.eq('2px'), 13 | 'border-style': 'solid', 14 | 'border-color': assert.compare.truthy(), 15 | 'border-width': assert.compare.gt(0), 16 | 'display': 'inline-block' 17 | }, 'styles are incorrect'); 18 | }) 19 | 20 | testButtonColor('yellow', '#ffffff', '#999900'); 21 | testButtonColor('blue', '#ffffff', '#3333aa'); 22 | testButtonColor('green', '#ffffff', '#33cc33'); 23 | testButtonColor('purple', '#ffffff', '#993399'); 24 | testButtonColor('pink', '#ffffff', '#cc6699'); 25 | testButtonColor('orange', '#ffffff', '#ff9933'); 26 | testButtonColor('brown', '#ffffff', '#995522'); 27 | 28 | }()); -------------------------------------------------------------------------------- /exercises/mixins/src/sass/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin btn-color($color) { 2 | 3 | } -------------------------------------------------------------------------------- /exercises/mixins/src/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | $hopbush: #c69 !default; 2 | $patina: #699 !default; 3 | $venus: #998099 !default; 4 | $nebula: #d2e1dd !default; 5 | 6 | $black: #000 !default; 7 | $white: #fff !default; 8 | 9 | $btn-disabled-opacity: 0.5 !default; 10 | 11 | -------------------------------------------------------------------------------- /exercises/mixins/src/sass/app.scss: -------------------------------------------------------------------------------- 1 | @import '_variables'; 2 | @import '_mixins'; 3 | 4 | .btn { 5 | display: inline-block; 6 | border: 1px solid transparent; 7 | white-space: nowrap; 8 | vertical-align: middle; 9 | border-radius: 2px; 10 | user-select: none; 11 | transition: box-shadow 0.3s; 12 | &:hover:not(:disabled) { 13 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.8); 14 | } 15 | &:disabled { 16 | opacity: 0.65; 17 | box-shadow: none; 18 | } 19 | &.btn-yellow { @include btn-color(#990); } 20 | &.btn-blue { @include btn-color(#33a); } 21 | &.btn-green { @include btn-color(#3c3); } 22 | &.btn-purple { @include btn-color(#939); } 23 | &.btn-pink { @include btn-color($hopbush);} 24 | &.btn-orange { @include btn-color(#f93); } 25 | &.btn-brown { @include btn-color(#952); } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /exercises/nesting/exercise.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Nesting & Parent Selectors I", 3 | "instructions": "Design a button that has styling for primary and secondary modes, including disabled state" 4 | } -------------------------------------------------------------------------------- /exercises/nesting/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |9 | 10 | 11 |
12 | 13 |14 | 15 | 16 |
17 | 18 |19 | 20 | 21 |
22 | 23 |24 | 25 | 26 |
27 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /exercises/nesting/public/tests.js: -------------------------------------------------------------------------------- 1 | test('Primary button', function(assert) { 2 | assert.hasStyle('.btn.btn-primary', { 3 | 'padding-left': '10px', 4 | 'padding-right': '10px', 5 | 'padding-top': '2px', 6 | 'padding-bottom': '2px', 7 | 'border-radius': assert.compare.eq('2px'), 8 | 'border-style': 'solid', 9 | 'border-color': assert.compare.truthy(), 10 | 'border-width': assert.compare.gt(0) 11 | }, 'styles are incorrect'); 12 | 13 | assert.hasStyle('.btn.btn-primary', { 14 | 'background-color': 'rgb(204, 68, 102)', 15 | 'color': 'rgb(255, 255, 255)', 16 | 'opacity': '1' 17 | }, 'colors for enabled state are incorrect'); 18 | assert.hasStyle('.btn.btn-primary:disabled', { 19 | 'opacity': '0.5' 20 | }, 'colors for enabled state are incorrect'); 21 | 22 | }); 23 | 24 | test('Secondary button', function(assert) { 25 | assert.hasStyle('.btn.btn-secondary', { 26 | 'padding-left': '10px', 27 | 'padding-right': '10px', 28 | 'padding-top': '2px', 29 | 'padding-bottom': '2px', 30 | }, 'padding is incorrect'); 31 | 32 | assert.hasStyle('.btn.btn-secondary', { 33 | 'background-color': 'rgb(237, 188, 200)', 34 | 'color': 'rgb(0, 0, 0)', 35 | 'opacity': '1' 36 | }, 'colors for enabled state are incorrect'); 37 | assert.hasStyle('.btn.btn-secondary:disabled', { 38 | 'opacity': '0.5' 39 | }, 'colors for enabled state are incorrect'); 40 | }); 41 | -------------------------------------------------------------------------------- /exercises/nesting/src/sass/app.scss: -------------------------------------------------------------------------------- 1 | body { 2 | h1 { 3 | color: #c46; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /exercises/parent/exercise.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Nesting & Parent Selectors II", 3 | "instructions": "Modify this layout so that adding the .right-nav class to the container results in the sidebar aligning to the right instead of the left" 4 | } -------------------------------------------------------------------------------- /exercises/parent/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /exercises/range/public/tests.js: -------------------------------------------------------------------------------- 1 | // (function() { 2 | // function testButtonColor(colorName, fontColor, bgColor) { 3 | // test(colorName + ' button', function(assert) { 4 | // assert.hasStyle('.btn.btn-' + colorName, { 5 | // color: fontColor, 6 | // 'background-color': bgColor 7 | // }, 'Colors are correct') 8 | // }); 9 | // } 10 | // test('First button', function(assert) { 11 | // assert.hasStyle('.btn', { 12 | // 'border-radius': assert.compare.eq('2px'), 13 | // 'border-style': 'solid', 14 | // 'border-color': assert.compare.truthy(), 15 | // 'border-width': assert.compare.gt(0), 16 | // 'display': 'inline-block' 17 | // }, 'styles are incorrect'); 18 | // }) 19 | 20 | // testButtonColor('yellow', '#ffffff', '#999900'); 21 | // testButtonColor('blue', '#ffffff', '#3333aa'); 22 | // testButtonColor('green', '#ffffff', '#33cc33'); 23 | // testButtonColor('purple', '#ffffff', '#993399'); 24 | // testButtonColor('pink', '#ffffff', '#cc6699'); 25 | // testButtonColor('orange', '#ffffff', '#ff9933'); 26 | // testButtonColor('brown', '#ffffff', '#995522'); 27 | 28 | // }()); -------------------------------------------------------------------------------- /exercises/range/src/sass/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin range-thumb() { 2 | 3 | } -------------------------------------------------------------------------------- /exercises/range/src/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | $hopbush: #c69 !default; 2 | $patina: #699 !default; 3 | $venus: #998099 !default; 4 | $nebula: #d2e1dd !default; 5 | 6 | $black: #000 !default; 7 | $white: #fff !default; 8 | 9 | $btn-disabled-opacity: 0.5 !default; 10 | 11 | -------------------------------------------------------------------------------- /exercises/range/src/sass/app.scss: -------------------------------------------------------------------------------- 1 | @import '_variables'; 2 | @import '_mixins'; 3 | 4 | input[type="range"] { 5 | // Add your styles here 6 | } -------------------------------------------------------------------------------- /exercises/tiny/exercise.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Control Flow - @for, @each", 3 | "instructions": "Build your own tiny CSS utility classes" 4 | } -------------------------------------------------------------------------------- /exercises/tiny/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |9 | 10 | 11 |
12 | 13 |14 | 15 | 16 |
17 | 18 |19 | 20 | 21 |
22 | 23 |24 | 25 | 26 |
27 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /exercises/variables/public/tests.js: -------------------------------------------------------------------------------- 1 | test('Primary button', function(assert) { 2 | assert.hasStyle('.btn.btn-primary', { 3 | 'padding-left': '10px', 4 | 'padding-right': '10px', 5 | 'padding-top': '2px', 6 | 'padding-bottom': '2px', 7 | 'border-radius': assert.compare.eq('2px'), 8 | 'border-style': 'solid', 9 | 'border-color': assert.compare.truthy(), 10 | 'border-width': assert.compare.gt(0) 11 | }, 'styles are incorrect'); 12 | 13 | assert.hasStyle('.btn.btn-primary', { 14 | 'background-color': 'rgb(204, 102, 153)', 15 | 'color': 'rgb(255, 255, 255)', 16 | 'opacity': '1' 17 | }, 'colors for enabled state are incorrect'); 18 | assert.hasStyle('.btn.btn-primary:disabled', { 19 | 'opacity': '0.5' 20 | }, 'colors for enabled state are incorrect'); 21 | 22 | }); 23 | 24 | test('Secondary button', function(assert) { 25 | assert.hasStyle('.btn.btn-secondary', { 26 | 'padding-left': '10px', 27 | 'padding-right': '10px', 28 | 'padding-top': '2px', 29 | 'padding-bottom': '2px', 30 | }, 'padding is incorrect'); 31 | 32 | assert.hasStyle('.btn.btn-secondary', { 33 | 'background-color': '#d2e1dd', 34 | 'color': 'rgb(0, 0, 0)', 35 | 'opacity': '1' 36 | }, 'colors for enabled state are incorrect'); 37 | assert.hasStyle('.btn.btn-secondary:disabled', { 38 | 'opacity': '0.5' 39 | }, 'colors for enabled state are incorrect'); 40 | }); 41 | -------------------------------------------------------------------------------- /exercises/variables/src/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | $hopbush: #c69 !default; 2 | $patina: #699 !default; 3 | $venus: #998099 !default; 4 | $nebula: #d2e1dd !default; 5 | 6 | $black: #000 !default; 7 | $white: #fff !default; 8 | 9 | $btn-disabled-opacity: 0.5 !default; 10 | 11 | -------------------------------------------------------------------------------- /exercises/variables/src/sass/app.scss: -------------------------------------------------------------------------------- 1 | body { 2 | h1 { 3 | color: #c69; 4 | } 5 | } 6 | .btn { 7 | padding: 2px 10px; 8 | border-radius: 2px; 9 | &.btn-primary { 10 | border: 1px solid rebeccapurple; 11 | background-color: lawngreen; 12 | color: #fff; 13 | } 14 | &.btn-secondary { 15 | border: 1px solid cadetblue; 16 | background-color: peru; 17 | color: #000; 18 | } 19 | &:disabled { 20 | opacity: 0.5; 21 | } 22 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mike-works/sass-fundamentals", 3 | "version": "0.0.0-development", 4 | "description": "A sass workshop project", 5 | "main": "index.js", 6 | "scripts": { 7 | "test:ci": "echo ok", 8 | "travis-deploy-once": "travis-deploy-once", 9 | "semantic-release": "semantic-release" 10 | }, 11 | "repository": "https://github.com/mike-works/sass-fundamentals.git", 12 | "keywords": [ 13 | "sass", 14 | "css" 15 | ], 16 | "author": "Mike North' + info.detail + ''; 127 | } 128 | li.innerHTML = s; 129 | _errorContainer.appendChild(li); 130 | } 131 | 132 | function onFail(info) { 133 | if (!_errorContainer) { 134 | setupErrorList(); 135 | } 136 | addErrorToList(info); 137 | } 138 | 139 | function test(name, cb) { 140 | try { 141 | cb(assert); 142 | } catch(e) { 143 | console.error(e); 144 | onFail(Object.assign(e, { name })); 145 | } 146 | }; 147 | 148 | function start() { 149 | window.test = test; 150 | }; 151 | window.onload = start(); 152 | }()); -------------------------------------------------------------------------------- /public/js/utils.js: -------------------------------------------------------------------------------- 1 | window.setTheme = function (th) { 2 | debugger; 3 | document.body.classList; 4 | } -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@mike-works/js-lib-renovate-config"] 3 | } 4 | -------------------------------------------------------------------------------- /run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const program = require('./src/program'); 4 | const server = require('./src/server'); 5 | 6 | server(program); -------------------------------------------------------------------------------- /run.bat: -------------------------------------------------------------------------------- 1 | node run -e %2 2 | -------------------------------------------------------------------------------- /src/exercise.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | 5 | function Exercise(name, opts) { 6 | this.name = name; 7 | }; 8 | 9 | function spaces(num) { 10 | let s = ''; 11 | for (let i = 0; i < num; i++) { 12 | s += ' '; 13 | } 14 | return s; 15 | } 16 | 17 | Exercise.prototype = { 18 | path: function() { 19 | return path.join(__dirname, '..', 'exercises', this.name); 20 | }, 21 | load: function () { 22 | this.info = JSON.parse(fs.readFileSync(path.join(this.path(), 'exercise.json')).toString()); 23 | return this; 24 | }, 25 | begin: function() { 26 | let w = 60; 27 | let spaceStr = spaces(w); 28 | console.log(''); 29 | console.log( 30 | chalk.bgBlue(spaceStr + '\n') + 31 | chalk.bgBlue.white(' STARTING EXERCISE: ') + 32 | chalk.bgBlue.yellow.bold(this.info.title) + 33 | chalk.bgBlue(spaces(w - 24 - this.info.title.length) + '\n') + 34 | chalk.bgBlue(spaceStr + '\n') 35 | ); 36 | console.log(this.info.instructions); 37 | console.log('\n'); 38 | return this; 39 | } 40 | } 41 | 42 | module.exports = Exercise; -------------------------------------------------------------------------------- /src/program.js: -------------------------------------------------------------------------------- 1 | const program = require('commander'); 2 | const fs = require('fs'); 3 | const chalk = require('chalk'); 4 | 5 | let opts = {}; 6 | 7 | let exercises = fs 8 | .readdirSync('./exercises') 9 | .map((x) => x.toLowerCase()); 10 | 11 | program 12 | .option('-e, --exercise