diff --git a/package-lock.json b/package-lock.json index d14a999..892ab4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,10 @@ "name": "personal-site", "version": "1.0.0", "devDependencies": { + "@fortawesome/fontawesome-free": "^6.1.1", "css-loader": "^6.7.1", "dart-sass": "^1.25.0", + "file-loader": "^6.2.0", "html-webpack-plugin": "^5.5.0", "mini-css-extract-plugin": "^2.6.0", "pug": "^2.0.4", @@ -31,6 +33,16 @@ "node": ">=10.0.0" } }, + "node_modules/@fortawesome/fontawesome-free": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.1.1.tgz", + "integrity": "sha512-J/3yg2AIXc9wznaVqpHVX3Wa5jwKovVF0AMYSnbmcXTiL3PpRPfF58pzWucCwEiCJBp+hCNRLWClTomD8SseKg==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", @@ -1578,6 +1590,52 @@ "node": ">=0.8.0" } }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/file-loader/node_modules/loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -4734,6 +4792,12 @@ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true }, + "@fortawesome/fontawesome-free": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.1.1.tgz", + "integrity": "sha512-J/3yg2AIXc9wznaVqpHVX3Wa5jwKovVF0AMYSnbmcXTiL3PpRPfF58pzWucCwEiCJBp+hCNRLWClTomD8SseKg==", + "dev": true + }, "@leichtgewicht/ip-codec": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", @@ -6006,6 +6070,35 @@ "websocket-driver": ">=0.5.1" } }, + "file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true + }, + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", diff --git a/package.json b/package.json index aa5310b..5ee83ef 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,10 @@ ], "author": "Daniel \"Daniel_I_Am\" de Cloet", "devDependencies": { + "@fortawesome/fontawesome-free": "^6.1.1", "css-loader": "^6.7.1", "dart-sass": "^1.25.0", + "file-loader": "^6.2.0", "html-webpack-plugin": "^5.5.0", "mini-css-extract-plugin": "^2.6.0", "pug": "^2.0.4", diff --git a/src/sass/_base.scss b/src/sass/_base.scss new file mode 100644 index 0000000..f5ca8c6 --- /dev/null +++ b/src/sass/_base.scss @@ -0,0 +1,19 @@ +html, +body { + background-color: #162028; + font-family: $font-family-sans-serif; + font-size: $font-size-base; + line-height: $line-height-base; + color: $text-color; + padding: 0; + margin: 0; +} + +a { + transition: color 100ms ease-in-out; + color: darken($text-color, 15%); + + &:hover { + color: darken($text-color, 5%); + } +} diff --git a/src/sass/_blog.scss b/src/sass/_blog.scss new file mode 100644 index 0000000..41d2e9f --- /dev/null +++ b/src/sass/_blog.scss @@ -0,0 +1,128 @@ +$article-image-size: 24px; + +section.blog-recent { + .header { + display: flex; + justify-content: space-between; + + .all-articles { + display: flex; + flex-direction: column; + justify-content: center; + + a { + text-decoration: none; + + &:after { + content: "ยป"; + } + } + } + } +} + +section.blog-recent, +section.blog { + .list { + .blog-items { + list-style: none; + padding: 0; + + .blog-item { + background-color: $background-lighter; + border-radius: 16px; + padding: 8px; + display: flex; + cursor: pointer; + user-select: none; + + transition: transform 250ms ease-in-out; + + &:not(:last-child) { + margin-bottom: 1rem; + } + + &:hover { + transform: translateX(2rem); + + @media (max-width: $xl) { + transform: translateX(1rem); + } + + @media (max-width: $lg) { + transform: translateX(0.5rem); + } + } + + .article-image { + max-width: $article-image-size; + max-height: $article-image-size; + + margin-right: 1rem; + } + + .article-text { + .article-title { + line-height: $article-image-size; + font-size: 1.2rem; + margin: 0; + } + + .article-desc, + .article-date, + .article-published { + display: block; + } + + .article-date { + color: darken($text-color, 15%); + } + + .article-published { + text-transform: uppercase; + font-size: 1rem; + color: red; + } + } + } + } + } +} + +section.blog-popular { + .popular-items { + list-style: none; + padding: 0; + + li { + display: grid; + grid-template-columns: 25px 1fr; + + &:not(:first-child) { + margin-bottom: 0.25rem; + } + + &:not(:last-child) { + margin-top: 0.25rem; + } + + .arrow { + display: flex; + flex-direction: column; + justify-content: center; + width: 25px; + } + + .link { + text-decoration: none; + font-size: 1.2rem; + } + } + } +} + +.blog-links { + &> :not(:first-child) { + margin-left: 1rem; + } +} diff --git a/src/sass/_blogedit.scss b/src/sass/_blogedit.scss new file mode 100644 index 0000000..a1b05de --- /dev/null +++ b/src/sass/_blogedit.scss @@ -0,0 +1,74 @@ +.blog-edit { + .article-form { + input, + textarea { + padding: .1rem; + width: calc(100% - .2rem); + margin: 0; + } + + .controls { + width: 100%; + } + + input { + border-radius: 5px; + } + + .md-editor { + margin-top: .5rem; + + :first-child { + border-top-left-radius: 5px; + border-top-right-radius: 5px; + } + + :last-child { + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + } + + &> * { + border-width: 3px; + } + + .controls { + background-color: $background-lighter; + border-color: $background-darker; + border-bottom-width: 0; + border-style: solid; + + ul { + display: flex; + list-style: none; + padding: .25rem; + margin: 0; + + li { + a { + display: inline-block; + width: 32px; + height: 32px; + + i { + width: 100%; + max-height: 100%; + font-size: 24px; + text-align: center; + vertical-align: baseline; + } + } + } + } + } + } + + input, + textarea, + button { + background-color: $background-lighter; + border-color: $background-darker; + color: $text-color; + } + } +} diff --git a/src/sass/_container.scss b/src/sass/_container.scss new file mode 100644 index 0000000..06d37be --- /dev/null +++ b/src/sass/_container.scss @@ -0,0 +1,55 @@ +// Source: https://github.com/twbs/bootstrap/blob/main/dist/css/bootstrap.css + +.container, +.container-fluid, +.container-xxl, +.container-xl, +.container-lg, +.container-md, +.container-sm { + padding-right: 0.75rem; + padding-left: 0.75rem; + margin-right: auto; + margin-left: auto; +} + +@media (min-width: $sm) { + .container-sm, + .container { + max-width: 540px; + } +} +@media (min-width: $md) { + .container-md, + .container-sm, + .container { + max-width: 720px; + } +} +@media (min-width: $lg) { + .container-lg, + .container-md, + .container-sm, + .container { + max-width: 960px; + } +} +@media (min-width: $xl) { + .container-xl, + .container-lg, + .container-md, + .container-sm, + .container { + max-width: 1140px; + } +} +@media (min-width: $xxl) { + .container-xxl, + .container-xl, + .container-lg, + .container-md, + .container-sm, + .container { + max-width: 1320px; + } +} diff --git a/src/sass/_fontawesome.scss b/src/sass/_fontawesome.scss new file mode 100644 index 0000000..2cb41a0 --- /dev/null +++ b/src/sass/_fontawesome.scss @@ -0,0 +1,5 @@ +$fa-font-path: '~@fortawesome/fontawesome-free/webfonts'; + +@import "~@fortawesome/fontawesome-free/scss/fontawesome.scss"; +@import "~@fortawesome/fontawesome-free/scss/brands.scss"; +@import "~@fortawesome/fontawesome-free/scss/solid.scss"; diff --git a/src/sass/_footer.scss b/src/sass/_footer.scss new file mode 100644 index 0000000..90500c1 --- /dev/null +++ b/src/sass/_footer.scss @@ -0,0 +1,52 @@ +$icon-size: 2rem; + +footer { + display: flex; + justify-content: space-between; + + @media (max-width: $sm) { + flex-direction: column; + + .credits { + text-align: center; + } + } + + .credits { + p { + a { + text-decoration: none; + } + } + } + + .social-links { + display: flex; + flex-direction: column; + justify-content: center; + + ul { + display: flex; + list-style: none; + justify-content: center; + padding: 0; + + li { + a { + text-decoration: none; + color: $text-color; + margin: 0.5rem; + + &:hover { + color: darken($text-color, 10%); + } + + i { + line-height: $icon-size; + font-size: $icon-size; + } + } + } + } + } +} diff --git a/src/sass/_header.scss b/src/sass/_header.scss new file mode 100644 index 0000000..b823e20 --- /dev/null +++ b/src/sass/_header.scss @@ -0,0 +1,78 @@ +$brand-height: 64px; + +header { + display: flex; + justify-content: space-between; + + .brand { + display: flex; + text-decoration: none; + color: $text-color; + user-select: none; + + .brand-logo { + width: auto; + height: $brand-height; + } + + .brand-name { + line-height: $brand-height; + vertical-align: middle; + font-size: 28px; + + margin-left: 2rem; + + @media (max-width: $sm) { + font-size: 20px; + margin-left: 1rem; + } + } + } + + .site-nav { + flex-grow: 1; + max-width: 250px; + + .nav-items { + display: flex; + justify-content: space-between; + list-style: none; + padding: 0; + + .nav-item { + text-align: center; + + a { + text-decoration: none; + font-size: 1.1rem; + color: $text-color; + + &:hover { + color: darken($text-color, 15%); + } + } + } + } + } + + @media (max-width: $md) { + flex-direction: column; + + .site-nav { + flex-grow: unset; + max-width: unset; + + .nav-items { + justify-content: unset; + + .nav-item { + flex-basis: 100%; + } + } + } + } +} + +#app > .header { + margin-bottom: 1rem; +} diff --git a/src/sass/_hero.scss b/src/sass/_hero.scss new file mode 100644 index 0000000..5856bf5 --- /dev/null +++ b/src/sass/_hero.scss @@ -0,0 +1,30 @@ +section.hero { + max-width: 60%; + margin-top: 4rem; + margin-bottom: 4rem; + + @media (max-width: $lg) { + max-width: 75%; + margin-top: 2rem; + margin-bottom: 2rem; + } + + @media (max-width: $md) { + max-width: 100%; + margin-top: 1rem; + } + + h1 { + color: $primary; + font-size: 2.5rem; + + @media (max-width: $md) { + font-size: 1.5rem; + } + } + + p { + font-size: 14px; + line-height: 2; + } +} diff --git a/src/sass/_home.scss b/src/sass/_home.scss new file mode 100644 index 0000000..19f89fc --- /dev/null +++ b/src/sass/_home.scss @@ -0,0 +1,56 @@ +.split-content { + display: flex; + + main { + margin-right: 4rem; + width: 75%; + } + + aside { + width: 25%; + } + + @media (max-width: $lg) { + flex-direction: column; + + main, + aside { + width: 100%; + } + } +} + +.home-header { + position: relative; + background-color: rgba($black, 0.5); + padding-bottom: 90px; + padding-top: 45px; + + @media (max-width: $md) { + padding-top: 30px; + } + + .wave { + overflow: hidden; + display: block; + position: absolute; + left: 0; + right: 0; + bottom: 0; + width: 100%; + height: 90px; + transform: translateY(1px); + z-index: 3; + + svg { + position: absolute; + left: -3%; + right: -3%; + bottom: 0; + width: 106%; + min-width: 600px; + + fill: $background; + } + } +} diff --git a/src/sass/_markdown.scss b/src/sass/_markdown.scss new file mode 100644 index 0000000..846453f --- /dev/null +++ b/src/sass/_markdown.scss @@ -0,0 +1,24 @@ +.markdown-editor { + width: 100%; + + .input { + resize: vertical; + } + + .submit { + padding: .5rem; + border-radius: 5px; + } + + .popup { + position: absolute; + background-color: $background-lighter; + border: 2px solid $background-darker; + color: $text-color; + padding: 1rem; + } + + .render { + width: 100%; + } +} diff --git a/src/sass/_pagination.scss b/src/sass/_pagination.scss new file mode 100644 index 0000000..5ec2439 --- /dev/null +++ b/src/sass/_pagination.scss @@ -0,0 +1,42 @@ +.pagination { + display: flex; + justify-content: flex-start; + + &:not(:last-of-type) { + margin-bottom: .5rem; + } + + button { + background-color: $background; + border: 1px solid white; + color: $text-color; + + height: 40px; + width: 120px; + padding: .5rem; + flex-grow: 1; + } + + button:not(.unselectable) { + cursor: pointer; + } + + button:not(.unselectable):hover, + button.active { + background-color: $background-lighter; + } + + button:not(:last-child) { + border-right: none; + } + + button:first-child { + border-top-left-radius: 15px; + border-bottom-left-radius: 15px; + } + + button:last-child { + border-top-right-radius: 15px; + border-bottom-right-radius: 15px; + } +} diff --git a/src/sass/_variables.scss b/src/sass/_variables.scss new file mode 100644 index 0000000..e3d8d93 --- /dev/null +++ b/src/sass/_variables.scss @@ -0,0 +1,32 @@ +// Typography +$font-family-sans-serif: "Nunito", sans-serif; +$font-size-base: 0.9rem; +$line-height-base: 1.6; + +// Base colors +$white: #ffffff; +$black: #000000; +$blue: #3490dc; +$indigo: #6574cd; +$purple: #9561e2; +$pink: #f66d9b; +$red: #e3342f; +$orange: #f6993f; +$yellow: #ffed4a; +$green: #38c172; +$teal: #4dc0b5; +$cyan: #6cb2eb; + +// Theme colors +$background: #162028; +$background-lighter: #1c2d35; +$background-darker: #0f171d; +$primary: #a1fab3; +$text-color: #ecf8ff; + +// Breakpoints +$sm: 576px; +$md: 768px; +$lg: 992px; +$xl: 1200px; +$xxl: 1400px; diff --git a/src/sass/main.scss b/src/sass/main.scss index 9e9baf9..042a73a 100644 --- a/src/sass/main.scss +++ b/src/sass/main.scss @@ -1,4 +1,21 @@ -html, -body { - background-color: black; -} +// Reusable variables +@import "variables"; + +// Plugins +@import "fontawesome"; + +// Base styling +@import "base"; + +// Generic styling +@import "container"; + +// Component specific styling +@import "header"; +@import "hero"; +@import "blog"; +@import "footer"; +@import "home"; +@import "pagination"; +@import "markdown"; +@import "blogedit"; diff --git a/src/snippets.pug b/src/snippets.pug new file mode 100644 index 0000000..e69de29 diff --git a/webpack.config.js b/webpack.config.js index e1ab03e..b6c14a4 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -55,6 +55,17 @@ module.exports = (env, argv) => { }, ], }, + { + test: /\.(ttf|otf|eot|svg|woff(2)?)$/, + use: [ + { + loader: 'file-loader', + options: { + outputPath: '../fonts/', + }, + } + ], + } ], }, resolve: {