diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4b48d59
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,112 @@
+# MACBOOK Default File
+.DS_Store
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# Next.js build output
+.next
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and *not* Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
+
+# others
+node_modules
+yarn.lock
+
\ No newline at end of file
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..138f45f
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,10 @@
+{
+ "printWidth": 100,
+ "tabWidth": 2,
+ "singleQuote": true,
+ "trailingComma": "all",
+ "bracketSpacing": true,
+ "semi": false,
+ "useTabs": false,
+ "endOfLine": "lf"
+}
diff --git a/1-Colors/index.html b/1-Colors/index.html
new file mode 100644
index 0000000..1022c2c
--- /dev/null
+++ b/1-Colors/index.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ Random Color Change
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/1-Colors/src/css/style.css b/1-Colors/src/css/style.css
new file mode 100644
index 0000000..97f38c1
--- /dev/null
+++ b/1-Colors/src/css/style.css
@@ -0,0 +1,29 @@
+html,
+body {
+ width: 100%;
+ height: 100vh;
+ overflow: hidden;
+}
+
+#app {
+ width: 100%;
+ height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.color-btn {
+ background-color: transparent;
+ border: 3.5px solid;
+ border-radius: 8px;
+ color: gray;
+ outline: none;
+ width: 80px;
+ height: 30px;
+ font-size: 15px;
+}
+
+.color-btn:hover {
+ background-color: #e6e6e6;
+}
diff --git a/1-Colors/src/js/App.js b/1-Colors/src/js/App.js
new file mode 100644
index 0000000..78f8dcb
--- /dev/null
+++ b/1-Colors/src/js/App.js
@@ -0,0 +1,17 @@
+export default function App({ $target }) {
+ const $background = document.getElementById('app')
+ const $button = document.createElement('button')
+ $button.className = 'color-btn'
+ $button.innerText = 'Click me'
+ $target.appendChild($button)
+
+ $button.addEventListener('click', () => {
+ onColorChange()
+ })
+
+ function onColorChange() {
+ const colors = ['#FC5C7D', '#6A82FB', '#38ef7d', '#fffbd5', 'eee8aa', '#ffdab9', '#fffaf0']
+ let num = Math.floor(Math.random() * colors.length)
+ $background.style.backgroundColor = colors[num]
+ }
+}
diff --git a/1-Colors/src/main.js b/1-Colors/src/main.js
new file mode 100644
index 0000000..7c7b3a8
--- /dev/null
+++ b/1-Colors/src/main.js
@@ -0,0 +1,7 @@
+import App from './js/App.js'
+
+const $app = document.querySelector('#app')
+
+new App({
+ $target: $app,
+})
diff --git a/2-Hex_Color/index.html b/2-Hex_Color/index.html
new file mode 100644
index 0000000..9df0995
--- /dev/null
+++ b/2-Hex_Color/index.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ Hex Colors gradient
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/2-Hex_Color/src/css/reset.css b/2-Hex_Color/src/css/reset.css
new file mode 100644
index 0000000..5bca465
--- /dev/null
+++ b/2-Hex_Color/src/css/reset.css
@@ -0,0 +1,129 @@
+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,
+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 {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+ display: block;
+}
+body {
+ line-height: 1;
+}
+ol,
+ul {
+ list-style: none;
+}
+blockquote,
+q {
+ quotes: none;
+}
+blockquote:before,
+blockquote:after,
+q:before,
+q:after {
+ content: '';
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+input:focus {
+ outline: none;
+}
+a {
+ color: inherit;
+ text-decoration: none;
+}
diff --git a/2-Hex_Color/src/css/style.css b/2-Hex_Color/src/css/style.css
new file mode 100644
index 0000000..7f515a4
--- /dev/null
+++ b/2-Hex_Color/src/css/style.css
@@ -0,0 +1,75 @@
+@import 'reset.css';
+
+:root {
+ --left: white;
+ --right: white;
+ --font: black;
+ --direction: to left;
+}
+
+html,
+body {
+ width: 100%;
+ height: 100vh;
+ overflow: hidden;
+}
+
+#app {
+ width: 100%;
+ height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background: linear-gradient(var(--direction), var(--left), var(--right));
+ -webkit-animation: colorchange 8s linear infinite alternate;
+}
+
+@-webkit-keyframes colorchange {
+ 0% {
+ color: white;
+ }
+ 30% {
+ color: gray;
+ }
+ 60% {
+ color: darkgray;
+ }
+ 100% {
+ color: black;
+ }
+}
+
+.description {
+ width: 900px;
+ font-size: 45px;
+ font-weight: bold;
+ margin-left: 3%;
+}
+
+.subDescription {
+ width: 800px;
+ font-size: 45px;
+ font-weight: bold;
+ margin-left: 7.5%;
+}
+
+.gradient {
+ width: 900px;
+ font-size: 35px;
+ font-weight: bold;
+ margin: 7% 0% 0% 7%;
+}
+
+.color_btn {
+ margin: 3% 0% 0% 43%;
+ width: 90px;
+ height: 50px;
+ font-weight: bold;
+ background: transparent;
+ border: 4px solid black;
+ border-radius: 4px;
+}
+
+.color_btn:hover {
+ background-color: var(--left);
+}
diff --git a/2-Hex_Color/src/js/App.js b/2-Hex_Color/src/js/App.js
new file mode 100644
index 0000000..e2f0623
--- /dev/null
+++ b/2-Hex_Color/src/js/App.js
@@ -0,0 +1,62 @@
+import { leftOption, rightOption, directOption } from '../utils/Constant.js'
+import Background from './Background.js'
+import Description from './Description.js'
+
+export default function App({ $target }) {
+ this.state = {
+ left: 'white',
+ right: 'white',
+ direction: 'to right',
+ }
+
+ const { left, right, direction } = this.state
+
+ const description = new Description({
+ $target,
+ initialState: {
+ left,
+ right,
+ direction,
+ },
+ onChangeColor: () => {
+ const colorRandomNum = Math.floor(Math.random() * leftOption.length)
+ const direcRandomNum = Math.floor(Math.random() * directOption.length)
+
+ const selectedLeft = leftOption[colorRandomNum]
+ const selectedRight = rightOption[colorRandomNum]
+ const selectedDirection = directOption[direcRandomNum]
+
+ this.setState({
+ left: selectedLeft,
+ right: selectedRight,
+ direction: selectedDirection,
+ })
+ },
+ })
+
+ const background = new Background({
+ $target,
+ initialState: {
+ left,
+ right,
+ direction,
+ },
+ })
+
+ this.setState = (nextState) => {
+ this.state = nextState
+
+ const { left, right, direction } = this.state
+ description.setState({
+ left,
+ right,
+ direction,
+ })
+
+ background.setState({
+ left,
+ right,
+ direction,
+ })
+ }
+}
diff --git a/2-Hex_Color/src/js/Background.js b/2-Hex_Color/src/js/Background.js
new file mode 100644
index 0000000..0dc603b
--- /dev/null
+++ b/2-Hex_Color/src/js/Background.js
@@ -0,0 +1,16 @@
+import { $setProperty } from '../utils/document.js'
+
+export default function Background({ $target, initialState }) {
+ this.state = initialState
+
+ this.setState = (nextState) => {
+ this.state = nextState
+ this.render()
+ }
+
+ this.render = () => {
+ $setProperty($target, '--left', this.state.left)
+ $setProperty($target, '--right', this.state.right)
+ $setProperty($target, '--direction', this.state.direction)
+ }
+}
diff --git a/2-Hex_Color/src/js/Description.js b/2-Hex_Color/src/js/Description.js
new file mode 100644
index 0000000..16773af
--- /dev/null
+++ b/2-Hex_Color/src/js/Description.js
@@ -0,0 +1,32 @@
+export default function Description({ $target, initialState, onChangeColor }) {
+ const $itemDiv = document.createElement('div')
+ $target.appendChild($itemDiv)
+
+ this.state = initialState
+
+ this.setState = (nextState) => {
+ this.state = nextState
+ this.render()
+ }
+
+ this.render = () => {
+ const { left, right, direction } = this.state
+
+ $itemDiv.innerHTML = `
+ CLICK THE BUTTON BELLOW TO GENERATE
+ A RANDOM HEX COLOR COMBINATION
+ background liner-gradient(${direction}, ${left}, ${right})
+
+ `
+ }
+
+ this.render()
+
+ $itemDiv.addEventListener('click', (e) => {
+ const { className } = e.target
+
+ if (className === 'color_btn') {
+ onChangeColor()
+ }
+ })
+}
diff --git a/2-Hex_Color/src/main.js b/2-Hex_Color/src/main.js
new file mode 100644
index 0000000..7c7b3a8
--- /dev/null
+++ b/2-Hex_Color/src/main.js
@@ -0,0 +1,7 @@
+import App from './js/App.js'
+
+const $app = document.querySelector('#app')
+
+new App({
+ $target: $app,
+})
diff --git a/2-Hex_Color/src/utils/Constant.js b/2-Hex_Color/src/utils/Constant.js
new file mode 100644
index 0000000..ce99761
--- /dev/null
+++ b/2-Hex_Color/src/utils/Constant.js
@@ -0,0 +1,29 @@
+export const leftOption = [
+ '#2193b0',
+ '#bdc3c7',
+ '#12c2e9',
+ '#1f4037',
+ '#8360c3',
+ '#E94057',
+ '#007991',
+]
+export const rightOption = [
+ '#6dd5ed',
+ '#2c3e50',
+ '#f64f59',
+ '#99f2c8',
+ '#2ebf91',
+ '#F27121',
+ '#78ffd6',
+]
+export const directOption = [
+ 'to right',
+ 'to left',
+ 'to top',
+ 'to bottom',
+ '45deg',
+ '90deg',
+ '75deg',
+ '15deg',
+ '60deg',
+]
diff --git a/2-Hex_Color/src/utils/document.js b/2-Hex_Color/src/utils/document.js
new file mode 100644
index 0000000..c0c2ded
--- /dev/null
+++ b/2-Hex_Color/src/utils/document.js
@@ -0,0 +1,2 @@
+export const $setProperty = ($target, startPoint, color) =>
+ $target.style.setProperty(startPoint, color)
diff --git a/3-Quote/index.html b/3-Quote/index.html
new file mode 100644
index 0000000..d447afc
--- /dev/null
+++ b/3-Quote/index.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/3-Quote/src/css/reset.css b/3-Quote/src/css/reset.css
new file mode 100644
index 0000000..5bca465
--- /dev/null
+++ b/3-Quote/src/css/reset.css
@@ -0,0 +1,129 @@
+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,
+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 {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+ display: block;
+}
+body {
+ line-height: 1;
+}
+ol,
+ul {
+ list-style: none;
+}
+blockquote,
+q {
+ quotes: none;
+}
+blockquote:before,
+blockquote:after,
+q:before,
+q:after {
+ content: '';
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+input:focus {
+ outline: none;
+}
+a {
+ color: inherit;
+ text-decoration: none;
+}
diff --git a/3-Quote/src/css/style.css b/3-Quote/src/css/style.css
new file mode 100644
index 0000000..579767e
--- /dev/null
+++ b/3-Quote/src/css/style.css
@@ -0,0 +1,59 @@
+@import 'reset.css';
+
+html,
+body {
+ width: 100%;
+ height: 100vh;
+ overflow: hidden;
+}
+
+.app {
+ width: 100%;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ background: linear-gradient(to right, #5d52a2, #3683b0, #5d52a2);
+}
+
+.quote_wrapper {
+ width: 60%;
+ height: 42%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ border: 10px solid #06bdc1;
+ border-radius: 4px;
+ background-color: #f4edea;
+}
+
+.quote_text {
+ width: 750px;
+ margin-top: 12%;
+ margin-left: 5%;
+ font-size: 26px;
+ line-height: 150%;
+ text-align: center;
+}
+
+.quote_author {
+ margin-top: 5%;
+ font-size: 25px;
+}
+
+button {
+ width: 140px;
+ height: 40px;
+ font-size: 16px;
+ margin-top: 3%;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ background-color: #17a2b8;
+ outline: none;
+}
+
+button:hover {
+ background-color: #138799;
+}
diff --git a/3-Quote/src/js/App.js b/3-Quote/src/js/App.js
new file mode 100644
index 0000000..ea1852c
--- /dev/null
+++ b/3-Quote/src/js/App.js
@@ -0,0 +1,15 @@
+import { request } from '../service/api.js'
+
+export default function App({ $target }) {
+ const $app = document.querySelector('.app')
+ const $text = document.querySelector('.quote_text')
+ const $author = document.querySelector('.quote_author')
+ const $button = document.querySelector('button')
+
+ $button.addEventListener('click', async (e) => {
+ const { quote, author } = await request()
+ $text.innerText = '"' + quote + '"'
+
+ $author.innerText = author ? '- ' + author : 'anonymous'
+ })
+}
diff --git a/3-Quote/src/main.js b/3-Quote/src/main.js
new file mode 100644
index 0000000..7c7b3a8
--- /dev/null
+++ b/3-Quote/src/main.js
@@ -0,0 +1,7 @@
+import App from './js/App.js'
+
+const $app = document.querySelector('#app')
+
+new App({
+ $target: $app,
+})
diff --git a/3-Quote/src/service/api.js b/3-Quote/src/service/api.js
new file mode 100644
index 0000000..833839a
--- /dev/null
+++ b/3-Quote/src/service/api.js
@@ -0,0 +1,13 @@
+export const request = async () => {
+ try {
+ const res = await fetch('https://free-quotes-api.herokuapp.com/')
+
+ if (!res.ok) {
+ throw new Error('Fail to fetch API data')
+ }
+
+ return await res.json()
+ } catch (e) {
+ alert(e.message)
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..2c3a1b3
--- /dev/null
+++ b/package.json
@@ -0,0 +1,16 @@
+{
+ "scripts": {
+ "start": "node main.js",
+ "lint": "eslint *.js",
+ "lint:fix": "eslint --fix *.js"
+ },
+ "devDependencies": {
+ "eslint": "^7.32.0",
+ "eslint-config-airbnb-base": "^14.2.1",
+ "eslint-config-google": "^0.14.0",
+ "eslint-config-prettier": "^8.3.0",
+ "eslint-plugin-import": "^2.24.0",
+ "eslint-plugin-prettier": "^3.4.0",
+ "prettier": "^2.3.2"
+ }
+}