├── .eslintrc.json ├── .gitignore ├── README.md ├── babel.config.js ├── codegrade_mvp.test.js ├── design-files └── desktop-example.png ├── jest.config.js ├── mocks ├── api.js ├── handlers.js ├── img │ ├── accent.png │ ├── cta.png │ └── logo.png ├── jest.globals.js └── server.js ├── package-lock.json ├── package.json ├── src ├── index.html ├── index.js ├── original.html └── styles.css └── webpack.config.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es2021": true, 6 | "jest": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "parserOptions": { 10 | "ecmaVersion": "latest" 11 | }, 12 | "rules": { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Microbundle cache 58 | .rpt2_cache/ 59 | .rts2_cache_cjs/ 60 | .rts2_cache_es/ 61 | .rts2_cache_umd/ 62 | 63 | # Optional REPL history 64 | .node_repl_history 65 | 66 | # Output of 'npm pack' 67 | *.tgz 68 | 69 | # Yarn Integrity file 70 | .yarn-integrity 71 | 72 | # dotenv environment variables file 73 | .env 74 | .env.test 75 | .env.production 76 | 77 | # parcel-bundler cache (https://parceljs.org/) 78 | .cache 79 | .parcel-cache 80 | 81 | # Next.js build output 82 | .next 83 | out 84 | 85 | # Nuxt.js build / generate output 86 | .nuxt 87 | dist 88 | 89 | # Gatsby files 90 | .cache/ 91 | # Comment in the public line in if your project uses Gatsby and not Next.js 92 | # https://nextjs.org/blog/next-9-1#public-directory-support 93 | # public 94 | 95 | # vuepress build output 96 | .vuepress/dist 97 | 98 | # Serverless directories 99 | .serverless/ 100 | 101 | # FuseBox cache 102 | .fusebox/ 103 | 104 | # DynamoDB Local files 105 | .dynamodb/ 106 | 107 | # TernJS port file 108 | .tern-port 109 | 110 | # Stores VSCode versions used for testing VSCode extensions 111 | .vscode-test 112 | 113 | # yarn v2 114 | .yarn/cache 115 | .yarn/unplugged 116 | .yarn/build-state.yml 117 | .yarn/install-state.gz 118 | .pnp.* 119 | 120 | # Mac cruft 121 | .DS_Store 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DOM I 2 | 3 | ## Project Description 4 | 5 | You are going to be emulating a content management system by controlling the content in the JavaScript file instead of the HTML file. This project is an exercise pointed at selecting elements and then updating them without touching the HTML file using the DOM. 6 | 7 | Compare `src/index.html` against `src/original.html` and notice how `index.html` is lacking text content and other things. The goal is to make the page look the same as `original.html` using JavaScript. You can find a screenshot of the goal in `design-files/desktop-example.png`. 8 | 9 | Inside `src/index.js` there is declared an object literal containing all the data needed to make the page look like the screenshot. Do not change this object! Below the declaration you should perform your DOM manipulations. Typically you would select an element and then use the data inside the object to add text content to it, or to change its attributes. Access the data using dot or square-bracket notation. 10 | 11 | **THE MOST IMPORTANT RULE: You cannot update the HTML file directly. You must use JavaScript alone.** 12 | 13 | ## Git Setup 14 | 15 | * [ ] Create a forked copy of this project. 16 | * [ ] Clone your OWN version of the repository. 17 | * [ ] Push commits: `git push origin main`. 18 | 19 | ## Running the project 20 | 21 | * [ ] Run `npm install` to download the project's dependencies. 22 | * [ ] Run `npm start` to launch the page on `http://localhost:3000`. 23 | * [ ] Run `npm test` to execute auto tests against your work (you'll need a new terminal window). 24 | 25 | ## MVP 26 | 27 | ### Create selectors to access the relevant elements 28 | 29 | * [ ] Declare variables pointing to the relevant DOM elements, using any of the selectors you have learned. 30 | 31 | ### Add text contents 32 | 33 | * [ ] Using your selectors, update the text contents of the relevant elements, matching the design file. 34 | * [ ] Find the correct texts for the elements inside the data object in `src/index.js`. 35 | 36 | ### Add class names 37 | 38 | * [ ] Give the anchor tags _inside the nav_ an italic style by adding the classname `italic` to them alone. 39 | * [ ] Give the anchor tag _inside the footer_ a bolder appearence by adding the classname `bold` to it alone. 40 | 41 | ### Add image sources 42 | 43 | * [ ] Make the img tags on the page display the correct images by editing their `src` attribute. 44 | * [ ] Find the correct URLs for the images inside the data object in `src/index.js`. 45 | 46 | ## Submission Format 47 | 48 | * [ ] Submit a link to your github repo in canvas. 49 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | testing: { // matches the `NODE_ENV=testing` in "test" script in package.json 4 | plugins: [ 5 | '@babel/plugin-transform-runtime', 6 | ], 7 | presets: [ 8 | [ 9 | '@babel/preset-env', 10 | { 11 | modules: 'commonjs', 12 | debug: false 13 | } 14 | ] 15 | ] 16 | } 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /codegrade_mvp.test.js: -------------------------------------------------------------------------------- 1 | const { server } = require('./mocks/server') 2 | const { screen } = require('@testing-library/dom') 3 | require('@testing-library/jest-dom/extend-expect') 4 | 5 | beforeAll(() => { 6 | server.listen() 7 | document.body.innerHTML = ` 8 |
9 |
10 | 18 | 19 |
20 |
21 |
22 |

23 | 24 |
25 | Image of a code snippet 26 |
27 |
28 |
29 |
30 |

31 |

32 |
33 |
34 |

35 |

36 |
37 |
38 | Image of code snippets across the screen 39 |
40 |
41 |

42 |

43 |
44 |
45 |

46 |

47 |
48 |
49 |

50 |

51 |
52 |
53 |
54 |
55 |

56 |

57 |

58 |

59 |
60 | 63 |
64 | ` 65 | require('./src/index.js') 66 | }) 67 | afterAll(() => { 68 | server.close() 69 | }) 70 | afterEach(() => { 71 | server.resetHandlers() 72 | }) 73 | 74 | describe('text contents', () => { 75 | test('[1] header nav links contain the correct text', () => { 76 | expect(screen.getByText('Services', { selector: 'nav a' })) 77 | expect(screen.getByText('Product', { selector: 'nav a' })) 78 | expect(screen.getByText('Vision', { selector: 'nav a' })) 79 | expect(screen.getByText('Features', { selector: 'nav a' })) 80 | expect(screen.getByText('About', { selector: 'nav a' })) 81 | expect(screen.getByText('Contact', { selector: 'nav a' })) 82 | }) 83 | test('[2] call to action elements contain the correct text', () => { 84 | expect(screen.getByText('DOM Is Awesome')) 85 | expect(screen.getByText('Get Started')) 86 | }) 87 | test('[3] main content headings contain the correct text', () => { 88 | expect(screen.getByText( 89 | 'Features', 90 | { selector: '.top-content .text-content:nth-of-type(1) h4' } 91 | )) 92 | expect(screen.getByText( 93 | 'About', 94 | { selector: '.top-content .text-content:nth-of-type(2) h4' } 95 | )) 96 | }) 97 | test('[4] main content paragraphs contain the correct text', () => { 98 | expect(screen.getByText( 99 | /Features content elementum magna eros/, 100 | { selector: '.top-content .text-content p' } 101 | )) 102 | expect(screen.getByText( 103 | /About content elementum magna eros/, 104 | { selector: '.top-content .text-content p' } 105 | )) 106 | }) 107 | test('[5] bottom content headings contain the correct text', () => { 108 | expect(screen.getByText( 109 | 'Services', 110 | { selector: '.bottom-content .text-content h4' } 111 | )) 112 | expect(screen.getByText( 113 | 'Product', 114 | { selector: '.bottom-content .text-content h4' } 115 | )) 116 | expect(screen.getByText( 117 | 'Vision', 118 | { selector: '.bottom-content .text-content h4' } 119 | )) 120 | }) 121 | test('[6] bottom content headings contain the correct text', () => { 122 | expect(screen.getByText( 123 | /Services content elementum magna eros/, 124 | { selector: '.bottom-content .text-content p' } 125 | )) 126 | expect(screen.getByText( 127 | /Product content elementum magna eros/, 128 | { selector: '.bottom-content .text-content p' } 129 | )) 130 | expect(screen.getByText( 131 | /Vision content elementum magna eros/, 132 | { selector: '.bottom-content .text-content p' } 133 | )) 134 | }) 135 | test('[7] contact information elements contain the correct text', () => { 136 | expect(screen.getByText( 137 | 'Contact', 138 | { selector: 'section.contact h4' } 139 | )) 140 | expect(screen.getByText( 141 | '123 Way 456 Street Somewhere, USA', 142 | { selector: 'section.contact p' } 143 | )) 144 | expect(screen.getByText('1 (888) 888-8888', 145 | { selector: 'section.contact p' } 146 | )) 147 | expect(screen.getByText( 148 | 'sales@greatidea.io', 149 | { selector: 'section.contact p' } 150 | )) 151 | }) 152 | test('[8] copyright information link contains the correct text', () => { 153 | expect(screen.getByText( 154 | /Copyright Great Idea/, 155 | { selector: 'footer a' } 156 | )) 157 | }) 158 | }) 159 | describe('img sources', () => { 160 | test('[9] header logo img has the correct src', () => { 161 | expect(screen.getByAltText(/Company logo/)) 162 | .toHaveAttribute('src', 'http://localhost:9000/img/logo.png') 163 | }) 164 | test('[10] call to action img has the correct src', () => { 165 | expect(screen.getByAltText('Image of a code snippet')) 166 | .toHaveAttribute('src', 'http://localhost:9000/img/cta.png') 167 | }) 168 | test('[11] middle img has the correct src', () => { 169 | expect(screen.getByAltText(/Image of code snippets/)) 170 | .toHaveAttribute('src', 'http://localhost:9000/img/accent.png') 171 | }) 172 | }) 173 | describe('class names', () => { 174 | test('[12] nav links have a class name of italic', () => { 175 | // screen.getAllByText(/[\s\S]*/, { selector: 'nav a' }) // TODO: research which 176 | for (let link of document.querySelectorAll('nav a')) { 177 | expect(link).toHaveAttribute('class', 'italic') 178 | } 179 | }) 180 | test('[13] no other links have a class name of italic', () => { 181 | expect(document.querySelector('footer a')).not.toHaveAttribute('class', 'italic') 182 | }) 183 | test('[14] the footer link has a class name of bold', () => { 184 | const link = document.querySelector('footer a') 185 | expect(link).toHaveAttribute('class', 'bold') 186 | }) 187 | test('[15] no other links have a class name of bold', () => { 188 | for (let link of document.querySelectorAll('nav a')) { 189 | expect(link).not.toHaveAttribute('class', 'bold') 190 | } 191 | }) 192 | }) 193 | -------------------------------------------------------------------------------- /design-files/desktop-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bloominstituteoftechnology/DOM-I/32a02b3e60583356e6eff04ab048da3360f4dbb6/design-files/desktop-example.png -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * For a detailed explanation regarding each configuration property, visit: 3 | * https://jestjs.io/docs/configuration 4 | */ 5 | 6 | module.exports = { 7 | // All imported modules in your tests should be mocked automatically 8 | // automock: false, 9 | 10 | // Stop running tests after `n` failures 11 | // bail: 0, 12 | 13 | // The directory where Jest should store its cached dependency information 14 | // cacheDirectory: "/private/var/folders/mq/wc13nlwj4gdgcyshdy5j1hgr0000gn/T/jest_dx", 15 | 16 | // Automatically clear mock calls and instances between every test 17 | // clearMocks: false, 18 | 19 | // Indicates whether the coverage information should be collected while executing the test 20 | // collectCoverage: false, 21 | 22 | // An array of glob patterns indicating a set of files for which coverage information should be collected 23 | // collectCoverageFrom: undefined, 24 | 25 | // The directory where Jest should output its coverage files 26 | // coverageDirectory: undefined, 27 | 28 | // An array of regexp pattern strings used to skip coverage collection 29 | // coveragePathIgnorePatterns: [ 30 | // "/node_modules/" 31 | // ], 32 | 33 | // Indicates which provider should be used to instrument code for coverage 34 | coverageProvider: "v8", 35 | 36 | // A list of reporter names that Jest uses when writing coverage reports 37 | // coverageReporters: [ 38 | // "json", 39 | // "text", 40 | // "lcov", 41 | // "clover" 42 | // ], 43 | 44 | // An object that configures minimum threshold enforcement for coverage results 45 | // coverageThreshold: undefined, 46 | 47 | // A path to a custom dependency extractor 48 | // dependencyExtractor: undefined, 49 | 50 | // Make calling deprecated APIs throw helpful error messages 51 | // errorOnDeprecated: false, 52 | 53 | // Force coverage collection from ignored files using an array of glob patterns 54 | // forceCoverageMatch: [], 55 | 56 | // A path to a module which exports an async function that is triggered once before all test suites 57 | // globalSetup: undefined, 58 | 59 | // A path to a module which exports an async function that is triggered once after all test suites 60 | // globalTeardown: undefined, 61 | 62 | // A set of global variables that need to be available in all test environments 63 | // globals: {}, 64 | 65 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. 66 | // maxWorkers: "50%", 67 | 68 | // An array of directory names to be searched recursively up from the requiring module's location 69 | // moduleDirectories: [ 70 | // "node_modules" 71 | // ], 72 | 73 | // An array of file extensions your modules use 74 | // moduleFileExtensions: [ 75 | // "js", 76 | // "jsx", 77 | // "ts", 78 | // "tsx", 79 | // "json", 80 | // "node" 81 | // ], 82 | 83 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module 84 | // moduleNameMapper: {}, 85 | 86 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 87 | // modulePathIgnorePatterns: [], 88 | 89 | // Activates notifications for test results 90 | // notify: false, 91 | 92 | // An enum that specifies notification mode. Requires { notify: true } 93 | // notifyMode: "failure-change", 94 | 95 | // A preset that is used as a base for Jest's configuration 96 | // preset: undefined, 97 | 98 | // Run tests from one or more projects 99 | // projects: undefined, 100 | 101 | // Use this configuration option to add custom reporters to Jest 102 | // reporters: undefined, 103 | 104 | // Automatically reset mock state between every test 105 | // resetMocks: false, 106 | 107 | // Reset the module registry before running each individual test 108 | // resetModules: false, 109 | 110 | // A path to a custom resolver 111 | // resolver: undefined, 112 | 113 | // Automatically restore mock state between every test 114 | // restoreMocks: false, 115 | 116 | // The root directory that Jest should scan for tests and modules within 117 | // rootDir: undefined, 118 | 119 | // A list of paths to directories that Jest should use to search for files in 120 | // roots: [ 121 | // "" 122 | // ], 123 | 124 | // Allows you to use a custom runner instead of Jest's default test runner 125 | // runner: "jest-runner", 126 | 127 | // The paths to modules that run some code to configure or set up the testing environment before each test 128 | "setupFiles": [ 129 | "./mocks/jest.globals.js" 130 | ], 131 | 132 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 133 | // setupFilesAfterEnv: [], 134 | 135 | // The number of seconds after which a test is considered as slow and reported as such in the results. 136 | // slowTestThreshold: 5, 137 | 138 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 139 | // snapshotSerializers: [], 140 | 141 | // The test environment that will be used for testing 142 | testEnvironment: "jsdom", 143 | 144 | // Options that will be passed to the testEnvironment 145 | // testEnvironmentOptions: {}, 146 | 147 | // Adds a location field to test results 148 | // testLocationInResults: false, 149 | 150 | // The glob patterns Jest uses to detect test files 151 | // testMatch: [ 152 | // "**/__tests__/**/*.[jt]s?(x)", 153 | // "**/?(*.)+(spec|test).[tj]s?(x)" 154 | // ], 155 | 156 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 157 | // testPathIgnorePatterns: [ 158 | // "/node_modules/" 159 | // ], 160 | 161 | // The regexp pattern or array of patterns that Jest uses to detect test files 162 | // testRegex: [], 163 | 164 | // This option allows the use of a custom results processor 165 | // testResultsProcessor: undefined, 166 | 167 | // This option allows use of a custom test runner 168 | // testRunner: "jest-circus/runner", 169 | 170 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 171 | // testURL: "http://localhost", 172 | 173 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 174 | // timers: "real", 175 | 176 | // A map from regular expressions to paths to transformers 177 | // transform: undefined, 178 | 179 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 180 | // transformIgnorePatterns: [ 181 | // "/node_modules/", 182 | // "\\.pnp\\.[^\\/]+$" 183 | // ], 184 | 185 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 186 | // unmockedModulePathPatterns: undefined, 187 | 188 | // Indicates whether each individual test should be reported during the run 189 | // verbose: undefined, 190 | 191 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 192 | // watchPathIgnorePatterns: [], 193 | 194 | // Whether to use watchman for file crawling 195 | // watchman: true, 196 | }; 197 | -------------------------------------------------------------------------------- /mocks/api.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const express = require('express') 3 | const cors = require('cors') 4 | 5 | const api = express() 6 | 7 | api.use(express.json()) 8 | api.use('/img', express.static(path.join(__dirname, 'img'))) 9 | api.use(cors()) 10 | 11 | api.listen(9000, () => { 12 | console.log('listening on 9000') 13 | }) 14 | -------------------------------------------------------------------------------- /mocks/handlers.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const fs = require('fs') 3 | const { rest } = require('msw') 4 | 5 | function getImage(req, res, ctx) { 6 | const imageBuffer = fs.readFileSync( 7 | path.resolve(__dirname, 'img', 'logo.png'), 8 | ) 9 | return res( 10 | ctx.set('Content-Length', imageBuffer.byteLength.toString()), 11 | ctx.set('Content-Type', 'image/png'), 12 | ctx.body(imageBuffer), 13 | ) 14 | } 15 | 16 | exports.handlers = [ 17 | rest.get('http://localhost:9000/img/*', getImage), 18 | ] 19 | -------------------------------------------------------------------------------- /mocks/img/accent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bloominstituteoftechnology/DOM-I/32a02b3e60583356e6eff04ab048da3360f4dbb6/mocks/img/accent.png -------------------------------------------------------------------------------- /mocks/img/cta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bloominstituteoftechnology/DOM-I/32a02b3e60583356e6eff04ab048da3360f4dbb6/mocks/img/cta.png -------------------------------------------------------------------------------- /mocks/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bloominstituteoftechnology/DOM-I/32a02b3e60583356e6eff04ab048da3360f4dbb6/mocks/img/logo.png -------------------------------------------------------------------------------- /mocks/jest.globals.js: -------------------------------------------------------------------------------- 1 | globalThis.fetch = require('node-fetch') 2 | -------------------------------------------------------------------------------- /mocks/server.js: -------------------------------------------------------------------------------- 1 | const { setupServer } = require('msw/node') 2 | const { handlers } = require('./handlers') 3 | 4 | exports.server = setupServer(...handlers) 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dom-i", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "start": "fkill :9000 :3000 -s && concurrently \"npm:backend\" \"npm:frontend\"", 6 | "frontend": "webpack serve --open", 7 | "backend": "node mocks/api.js", 8 | "test": "cross-env NODE_ENV=testing jest" 9 | }, 10 | "devDependencies": { 11 | "@babel/core": "7.17.5", 12 | "@babel/plugin-transform-runtime": "7.17.0", 13 | "@babel/preset-env": "7.16.11", 14 | "@testing-library/dom": "8.11.3", 15 | "@testing-library/jest-dom": "5.16.2", 16 | "@types/jest": "27.4.1", 17 | "babel-loader": "8.2.3", 18 | "concurrently": "7.0.0", 19 | "cross-env": "7.0.3", 20 | "eslint": "8.10.0", 21 | "fkill-cli": "7.1.0", 22 | "html-loader": "3.1.0", 23 | "html-webpack-plugin": "5.5.0", 24 | "jest": "27.5.1", 25 | "msw": "0.38.1", 26 | "nodemon": "2.0.15", 27 | "webpack": "5.69.1", 28 | "webpack-cli": "4.9.2", 29 | "webpack-dev-server": "4.7.4" 30 | }, 31 | "dependencies": { 32 | "cors": "2.8.5", 33 | "express": "4.17.3" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/bloominstituteoftechnology/DOM-I.git" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Great Idea! 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 24 | 25 |
26 |
27 |
28 |

29 | 30 |
31 | Image of a code snippet 32 |
33 |
34 |
35 |
36 |

37 |

38 |
39 |
40 |

41 |

42 |
43 |
44 | Image of code snippets across the screen 45 |
46 |
47 |

48 |

49 |
50 |
51 |

52 |

53 |
54 |
55 |

56 |

57 |
58 |
59 |
60 |
61 |

62 |

63 |

64 |

65 |
66 |
67 | 68 |
69 |
70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const siteContent = { // DO NOT CHANGE THIS OBJECT 2 | "nav": { 3 | "nav-item-1": "Services", 4 | "nav-item-2": "Product", 5 | "nav-item-3": "Vision", 6 | "nav-item-4": "Features", 7 | "nav-item-5": "About", 8 | "nav-item-6": "Contact", 9 | }, 10 | "cta": { 11 | "h1": "DOM Is Awesome", 12 | "button": "Get Started", 13 | }, 14 | "main-content": { 15 | "features-h4": "Features", 16 | "features-content": "Features content elementum magna eros, ac posuere elvit tempus et. Suspendisse vel tempus odio, in interdutm nisi. Suspendisse eu ornare nisl. Nullam convallis augue justo, at imperdiet metus scelerisque quis.", 17 | "about-h4": "About", 18 | "about-content": "About content elementum magna eros, ac posuere elvit tempus et. Suspendisse vel tempus odio, in interdutm nisi. Suspendisse eu ornare nisl. Nullam convallis augue justo, at imperdiet metus scelerisque quis.", 19 | "services-h4": "Services", 20 | "services-content": "Services content elementum magna eros, ac posuere elvit tempus et. Suspendisse vel tempus odio, in interdutm nisi. Suspendisse eu ornare nisl. Nullam convallis augue justo, at imperdiet metus scelerisque quis.", 21 | "product-h4": "Product", 22 | "product-content": "Product content elementum magna eros, ac posuere elvit tempus et. Suspendisse vel tempus odio, in interdutm nisi. Suspendisse eu ornare nisl. Nullam convallis augue justo, at imperdiet metus scelerisque quis.", 23 | "vision-h4": "Vision", 24 | "vision-content": "Vision content elementum magna eros, ac posuere elvit tempus et. Suspendisse vel tempus odio, in interdutm nisi. Suspendisse eu ornare nisl. Nullam convallis augue justo, at imperdiet metus scelerisque quis.", 25 | }, 26 | "contact": { 27 | "contact-h4": "Contact", 28 | "address": "123 Way 456 Street Somewhere, USA", 29 | "phone": "1 (888) 888-8888", 30 | "email": "sales@greatidea.io", 31 | }, 32 | "footer": { 33 | "copyright": "Copyright Great Idea! 2021", 34 | }, 35 | "images": { 36 | "logo-img": "http://localhost:9000/img/logo.png", 37 | "cta-img": "http://localhost:9000/img/cta.png", 38 | "accent-img": "http://localhost:9000/img/accent.png", 39 | }, 40 | }; 41 | 42 | console.log('project wired!') 43 | -------------------------------------------------------------------------------- /src/original.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Great Idea! 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 24 | 25 |
26 |
27 |
28 |

DOM Is Awesome

29 | 30 |
31 | Image of a code snippet 32 |
33 |
34 |
35 |
36 |

Features

37 |

Features content elementum magna eros, ac posuere elvit tempus et. Suspendisse vel tempus odio, 38 | in interdutm nisi. Suspendisse eu ornare nisl. Nullam convallis augue justo, at imperdiet metus 39 | scelerisque quis.

40 |
41 |
42 |

About

43 |

About content elementum magna eros, ac posuere elvit tempus et. Suspendisse vel tempus odio, in 44 | interdutm nisi. Suspendisse eu ornare nisl. Nullam convallis augue justo, at imperdiet metus 45 | scelerisque quis.

46 |
47 |
48 | Image of code snippets across the screen 50 |
51 |
52 |

Services

53 |

Services content elementum magna eros, ac posuere elvit tempus et. Suspendisse vel tempus odio, 54 | in interdutm nisi. Suspendisse eu ornare nisl. Nullam convallis augue justo, at imperdiet metus 55 | scelerisque quis.

56 |
57 |
58 |

Product

59 |

Product content elementum magna eros, ac posuere elvit tempus et. Suspendisse vel tempus odio, in 60 | interdutm nisi. Suspendisse eu ornare nisl. Nullam convallis augue justo, at imperdiet metus 61 | scelerisque quis.

62 |
63 |
64 |

Vision

65 |

Vision content elementum magna eros, ac posuere elvit tempus et. Suspendisse vel tempus odio, in 66 | interdutm nisi. Suspendisse eu ornare nisl. Nullam convallis augue justo, at imperdiet metus 67 | scelerisque quis.

68 |
69 |
70 |
71 |
72 |

Contact

73 |

123 Way 456 Street Somewhere, USA

74 |

1 (888) 888-8888

75 |

sales@greatidea.io

76 |
77 | 80 |
81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { 7 | margin: 0; 8 | padding: 0; 9 | border: 0; 10 | font-size: 100%; 11 | font: inherit; 12 | vertical-align: baseline; 13 | } 14 | 15 | /* HTML5 display-role reset for older browsers */ 16 | 17 | article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { 18 | display: block; 19 | } 20 | 21 | body { 22 | line-height: 1; 23 | } 24 | 25 | ol, ul { 26 | list-style: none; 27 | } 28 | 29 | blockquote, q { 30 | quotes: none; 31 | } 32 | 33 | blockquote:before, blockquote:after, q:before, q:after { 34 | content: ''; 35 | content: none; 36 | } 37 | 38 | table { 39 | border-collapse: collapse; 40 | border-spacing: 0; 41 | } 42 | 43 | /* Set every element's box-sizing to border-box */ 44 | 45 | * { 46 | box-sizing: border-box; 47 | } 48 | 49 | html, body { 50 | height: 100%; 51 | font-family: 'Titillium Web', sans-serif; 52 | } 53 | 54 | h1, h2, h3, h4, h5 { 55 | font-family: 'Bangers', cursive; 56 | letter-spacing: 1px; 57 | margin-bottom: 15px; 58 | } 59 | 60 | .container { 61 | width: 880px; 62 | margin: auto; 63 | } 64 | 65 | /* Header Styles */ 66 | 67 | header { 68 | width: 100%; 69 | display: flex; 70 | } 71 | 72 | header nav { 73 | margin-top: 40px; 74 | width: 610px; 75 | justify-content: space-between; 76 | display: flex; 77 | } 78 | 79 | header nav a { 80 | text-decoration: none; 81 | color: #b0b0b0; 82 | } 83 | 84 | header nav a.italic { 85 | font-style: italic; 86 | } 87 | 88 | header nav a:hover { 89 | text-decoration: underline; 90 | color: black; 91 | } 92 | 93 | header .logo { 94 | align-self: flex-end; 95 | margin: 28px 0 0 91px; 96 | } 97 | 98 | /* Call To Action (CTA) Styles */ 99 | 100 | .cta { 101 | display: flex; 102 | margin-top: 72px; 103 | justify-content: flex-end; 104 | } 105 | 106 | .cta .cta-text { 107 | font-size: 72px; 108 | text-align: center; 109 | letter-spacing: 5px; 110 | display: flex; 111 | justify-content: center; 112 | flex-flow: column; 113 | align-items: center; 114 | margin: 0 82px 0 0; 115 | } 116 | 117 | .cta .cta-text button { 118 | border: 1px double black; 119 | font-size: 16px; 120 | background: white; 121 | width: 170px; 122 | height: 32px; 123 | margin-top: 10px; 124 | cursor: pointer; 125 | } 126 | 127 | .cta .cta-text button:hover { 128 | background: black; 129 | color: white; 130 | } 131 | 132 | /* Main Content Styles */ 133 | 134 | .main-content { 135 | border-top: 2px solid black; 136 | border-bottom: 2px solid black; 137 | margin: 75px 0 35px 0; 138 | padding: 35px 0; 139 | font-size: 16px; 140 | line-height: 18px; 141 | } 142 | 143 | .main-content .top-content { 144 | display: flex; 145 | justify-content: space-around; 146 | } 147 | 148 | .main-content .middle-img { 149 | display: block; 150 | margin: 30px auto; 151 | } 152 | 153 | .main-content .bottom-content { 154 | display: flex; 155 | justify-content: space-around; 156 | } 157 | 158 | .main-content .bottom-content p { 159 | padding-right: 20px; 160 | } 161 | 162 | /* Contact Styles */ 163 | 164 | .contact p { 165 | margin: 15px 0; 166 | } 167 | 168 | /* Footer Styles */ 169 | 170 | footer { 171 | text-align: center; 172 | margin: 40px 0 20px; 173 | } 174 | 175 | footer a { 176 | text-decoration: none; 177 | color: #b0b0b0; 178 | } 179 | 180 | footer a.bold { 181 | font-weight: bold; 182 | } 183 | 184 | /* Tablet */ 185 | 186 | @media (max-width: 768px) { 187 | .container { 188 | width: 768px; 189 | } 190 | header { 191 | flex-wrap: wrap; 192 | } 193 | header nav { 194 | width: 768px; 195 | padding: 0 30px; 196 | } 197 | header .logo { 198 | order: -1; 199 | display: block; 200 | margin: 30px auto 0; 201 | } 202 | .cta { 203 | justify-content: center; 204 | } 205 | .cta .cta-text { 206 | margin: 0; 207 | } 208 | .cta img { 209 | display: none; 210 | } 211 | .main-content .middle-img { 212 | width: 100%; 213 | } 214 | .main-content { 215 | margin: 75px 20px 35px; 216 | } 217 | .text-content { 218 | margin: 0 10px; 219 | } 220 | .contact { 221 | margin: 0 30px; 222 | } 223 | } 224 | 225 | /* Phone */ 226 | 227 | @media (max-width: 400px) { 228 | .container { 229 | width: 400px; 230 | } 231 | header { 232 | flex-wrap: wrap; 233 | justify-content: center; 234 | } 235 | header nav { 236 | width: 100%; 237 | display: flex; 238 | flex-direction: column; 239 | margin: 0; 240 | align-items: center; 241 | padding: 0; 242 | } 243 | header nav a { 244 | width: 100%; 245 | display: block; 246 | text-align: center; 247 | padding: 20px 0; 248 | font-size: 20px; 249 | border-bottom: 1px solid gray; 250 | } 251 | header nav a:first-child { 252 | border-top: 1px solid gray; 253 | } 254 | header nav a:hover { 255 | background: gray; 256 | } 257 | header .logo { 258 | margin: 20px 0; 259 | } 260 | .cta { 261 | margin-top: 50px; 262 | flex-flow: column; 263 | align-items: center; 264 | } 265 | .cta img { 266 | max-width: 80%; 267 | } 268 | .main-content .top-content { 269 | justify-content: space-around; 270 | flex-direction: column; 271 | height: 300px; 272 | } 273 | .main-content .bottom-content { 274 | flex-wrap: wrap; 275 | align-content: space-between; 276 | height: 440px; 277 | } 278 | .text-content { 279 | margin: 0; 280 | } 281 | .contact { 282 | margin: 20px; 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const HtmlWebpackPlugin = require('html-webpack-plugin') 3 | 4 | module.exports = { 5 | entry: './src/index.js', 6 | mode: 'development', 7 | output: { 8 | filename: 'index.js', 9 | publicPath: '/', 10 | path: path.resolve(__dirname, 'dist'), 11 | }, 12 | devtool: 'source-map', 13 | plugins: [ 14 | new HtmlWebpackPlugin({ 15 | template: 'src/index.html', 16 | }), 17 | ], 18 | devServer: { 19 | static: path.join(__dirname, 'dist'), 20 | compress: true, 21 | port: 3000, 22 | }, 23 | module: { 24 | rules: [ 25 | { 26 | test: /\.html$/i, 27 | exclude: /node_modules/, 28 | loader: 'html-loader', 29 | }, 30 | { 31 | test: /\.m?js$/, 32 | exclude: /node_modules/, 33 | use: { 34 | loader: 'babel-loader', 35 | options: { 36 | 'presets': [['@babel/preset-env', { targets: { chrome: '80' } }]], 37 | } 38 | }, 39 | }, 40 | ], 41 | }, 42 | } 43 | --------------------------------------------------------------------------------