diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 89c5fd9..c2da13e 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,42 +2,20 @@ - - - - - - - - - - - + - - - + + - - - - - - - - - - - - + + + - - - + @@ -82,19 +60,19 @@ + + + + + + + - - - - - - - diff --git a/src/framework/cache.ts b/src/framework/cache.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/framework/helper/animations.ts b/src/framework/helper/animations.ts new file mode 100644 index 0000000..d03fcf3 --- /dev/null +++ b/src/framework/helper/animations.ts @@ -0,0 +1,5 @@ +export const turnOnAnimations = () => + document.body.parentElement.setAttribute('animations', 'on'); + +export const turnOffAnimations = () => + document.body.parentElement.setAttribute('animations', 'off'); diff --git a/src/framework/polyfills.ts b/src/framework/helper/polyfills.ts similarity index 89% rename from src/framework/polyfills.ts rename to src/framework/helper/polyfills.ts index bc31a07..e1113a1 100644 --- a/src/framework/polyfills.ts +++ b/src/framework/helper/polyfills.ts @@ -1,6 +1,6 @@ export const addImul = () => { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul if (!Math.imul) - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul Math.imul = function(opA, opB) { opB |= 0; // ensure that opB is an integer. opA will automatically be coerced. // floating points give us 53 bits of precision to work with plus 1 sign bit diff --git a/src/framework/helper/random.ts b/src/framework/helper/random.ts index aad6370..d7f51ff 100644 --- a/src/framework/helper/random.ts +++ b/src/framework/helper/random.ts @@ -1,4 +1,4 @@ -import { addImul } from '../polyfills'; +import { addImul } from './polyfills'; export class Random { public constructor(private seed: number) { diff --git a/src/framework/primitives/implementations/anchor.ts b/src/framework/primitives/implementations/anchor.ts index 30bc98e..b7ce2f7 100644 --- a/src/framework/primitives/implementations/anchor.ts +++ b/src/framework/primitives/implementations/anchor.ts @@ -8,6 +8,10 @@ export class Anchor implements Primitive { ) {} public toHTML(): html { - return `${this.text}`; + return `${this.text}`; } } diff --git a/src/index.html b/src/index.html index 2e1f704..9c9f477 100644 --- a/src/index.html +++ b/src/index.html @@ -12,7 +12,8 @@ - + + Portfolio - AndrĂ¡s Schmelczer diff --git a/src/page/background/background.scss b/src/page/background/background.scss index ce91f5a..07ca13f 100644 --- a/src/page/background/background.scss +++ b/src/page/background/background.scss @@ -25,7 +25,9 @@ } transition: transform map_get($vars, $long-transition-time), - opacity map_get($vars, $long-transition-time); + opacity map_get($vars, $long-transition-time), + background-color map_get($vars, $long-transition-time); + will-change: transform, opacity; animation: fade-in 1s linear; @keyframes fade-in { diff --git a/src/page/footer/footer.scss b/src/page/footer/footer.scss index b1855ff..83d082a 100644 --- a/src/page/footer/footer.scss +++ b/src/page/footer/footer.scss @@ -49,7 +49,7 @@ width: map_get($vars, $body-width); h6 { - @include insignificant-font(); + @include special-text-font($vars); display: inline; opacity: 0.75; } diff --git a/src/page/theme-switcher/theme-switcher.html.ts b/src/page/theme-switcher/theme-switcher.html.ts index ce0a54b..b5c9bd8 100644 --- a/src/page/theme-switcher/theme-switcher.html.ts +++ b/src/page/theme-switcher/theme-switcher.html.ts @@ -3,5 +3,5 @@ import { html } from '../../model/misc'; import './theme-switcher.scss'; export const generate = (): html => ` - + `; diff --git a/src/page/theme-switcher/theme-switcher.scss b/src/page/theme-switcher/theme-switcher.scss index bbaad22..6f10ebd 100644 --- a/src/page/theme-switcher/theme-switcher.scss +++ b/src/page/theme-switcher/theme-switcher.scss @@ -5,30 +5,40 @@ input[type='checkbox']#theme-switcher { @include on-large-screen { position: fixed; - top: map_get($vars, $large-margin); - right: map_get($vars, $large-margin); + top: map_get($vars, $normal-margin); + right: map_get($vars, $normal-margin); } @include on-small-screen { position: relative; - margin-top: map_get($vars, $small-margin); + margin-top: map_get($vars, $normal-margin); } - $size: map_get($vars, $icon-size); - $small-size: 4 / 5 * $size; + background-color: map_get($vars, $accent-color); z-index: 10; + cursor: pointer; -webkit-appearance: none; -moz-appearance: none; + $size: map_get($vars, $icon-size); width: 2 * $size; height: $size; + $icon-size: 0.7 * $size; + $margin: ($size - $icon-size) / 2; border-radius: 1000px; - box-shadow: map_get($vars, $shadow1), map_get($vars, $shadow2); + box-shadow: inset 0 0 10px 2px rgba(0, 0, 0, 0.175), + inset 0 0 1px rgba(0, 0, 0, 0.4); - cursor: pointer; + &:before { + @include square($icon-size); + } + + &:after { + @include square($icon-size * 0.8); + } &:before, &:after { @@ -37,53 +47,47 @@ display: block; border-radius: 1000px; - @include square($size); + top: 50%; + transform: translateY(-50%); - transition: height map_get($vars, $long-transition-time), - width map_get($vars, $long-transition-time), - left map_get($vars, $long-transition-time), - top map_get($vars, $long-transition-time), - transform map_get($vars, $long-transition-time), + transition: transform map_get($vars, $long-transition-time), background-color map_get($vars, $long-transition-time); } - &:before { - left: 0; - background-color: transparent; - } + &:not(:checked) { + &:before { + transform: translateY(-50%) translateX(3 * $margin + $icon-size); - &:after { - $delta: 4px; - top: -$delta / 2; - left: $size; - @include square($size + $delta); - background-color: map_get($vars, $theme-switcher-color); + animation: shine 3s linear alternate infinite; + background-color: map_get($vars, $theme-switcher-foreground); + @keyframes shine { + from { + filter: brightness(1.01); + box-shadow: 0 0 4px 2px map_get($vars, $theme-switcher-foreground); + } - animation: shine 3s linear alternate infinite; - - @keyframes shine { - from { - filter: brightness(1.01); - box-shadow: 0 0 4px 2px map_get($vars, $theme-switcher-color); + to { + filter: brightness(1.1); + box-shadow: 0 0 15px 2px map_get($vars, $theme-switcher-foreground); + } } + } - to { - filter: brightness(1.1); - box-shadow: 0 0 15px 2px map_get($vars, $theme-switcher-color); - } + &:after { + background-color: transparent; + transform: translateY(-50%) translateX($size * 2 - $icon-size); } } &:checked { &:before { background-color: map_get($vars, $normal-text-color); + transform: translateY(-50%) translateX($margin); } + &:after { - @include square($small-size); - $offset: $small-size / 5.5; - left: $size - $small-size + $offset; - top: ($size - $small-size) / 2; - background-color: map_get($vars, $background); + background-color: map_get($vars, $accent-color); + transform: translateY(-50%) translateX($margin + $icon-size * 0.33); } } diff --git a/src/page/theme-switcher/theme-switcher.ts b/src/page/theme-switcher/theme-switcher.ts index 08d43fe..80c97c1 100644 --- a/src/page/theme-switcher/theme-switcher.ts +++ b/src/page/theme-switcher/theme-switcher.ts @@ -8,19 +8,33 @@ import { } from '../../framework/helper/dark-mode'; import { PageEvent, PageEventType } from '../../framework/page-event'; import { EventBroadcaster } from '../../framework/event-broadcaster'; +import { + turnOffAnimations, + turnOnAnimations, +} from '../../framework/helper/animations'; export class PageThemeSwitcher extends PageElement { + private static readonly LOCAL_STORAGE_KEY = 'dark-mode'; + public constructor() { super(createElement(generate())); - if (isSystemLevelDarkModeEnabled()) { + + const storedIsDark = PageThemeSwitcher.loadFromLocalStorage(); + const isDark = storedIsDark ? storedIsDark : isSystemLevelDarkModeEnabled(); + + if (isDark) { (this.element as HTMLInputElement).checked = true; + turnOffAnimations(); + turnOnDarkMode(); + setTimeout(() => turnOnAnimations(), 0); + } else { + turnOnLightMode(); } this.element.onchange = this.handleThemeChange.bind(this); } protected handleEvent(event: PageEvent, parent: EventBroadcaster) { if (event.type === PageEventType.onLoad) { - console.log('a'); this.handleThemeChange(); } } @@ -36,5 +50,21 @@ export class PageThemeSwitcher extends PageElement { type: PageEventType.pageThemeChanged, data: isDark, }); + + PageThemeSwitcher.saveToLocalStorage(isDark); + } + + private static saveToLocalStorage(darkModeEnabled: boolean) { + window.localStorage?.setItem( + PageThemeSwitcher.LOCAL_STORAGE_KEY, + JSON.stringify(darkModeEnabled) + ); + } + + private static loadFromLocalStorage(): boolean | null { + return JSON.parse( + window.localStorage?.getItem(PageThemeSwitcher.LOCAL_STORAGE_KEY) || + 'null' + ); } } diff --git a/src/page/timeline/timeline-element/timeline-element.scss b/src/page/timeline/timeline-element/timeline-element.scss index 6b78212..00be40e 100644 --- a/src/page/timeline/timeline-element/timeline-element.scss +++ b/src/page/timeline/timeline-element/timeline-element.scss @@ -15,6 +15,7 @@ &:before { content: ''; @include square(map_get($vars, $icon-size)); + box-sizing: content-box; position: absolute; left: calc( -0.5 * #{map_get($vars, $icon-size)} - @@ -26,8 +27,7 @@ } .date { - @include insignificant-font(); - color: map_get($vars, $accent-color); + @include special-text-font($vars); } } @@ -77,8 +77,10 @@ .card { @include card-base($vars); + border-radius: map_get($vars, $border-radius); background-color: map_get($vars, $card-color); + overflow: hidden; & > *:not(:first-child) { @@ -116,10 +118,7 @@ .show-less { opacity: 0; visibility: hidden; - position: absolute; - left: 50%; - top: 50%; - transform: translateX(-50%) translateY(-50%); + @include absolute-center(); } } } diff --git a/src/style/a.scss b/src/style/a.scss index 8b635f8..f61fcc9 100644 --- a/src/style/a.scss +++ b/src/style/a.scss @@ -3,11 +3,10 @@ @include responsive() using ($vars) { a { - @include insignificant-font(); + @include special-text-font($vars); text-decoration: none; position: relative; cursor: pointer; - color: map_get($vars, $accent-color); display: inline-block; overflow: hidden; diff --git a/src/style/fonts.scss b/src/style/fonts.scss index 2cd92bf..2ec9bff 100644 --- a/src/style/fonts.scss +++ b/src/style/fonts.scss @@ -1,65 +1,65 @@ // https://google-webfonts-helper.herokuapp.com/fonts/montserrat?subsets=latin // add font-display: swap; - +/* /* lato-regular - latin */ @font-face { - font-family: "Lato"; + font-family: 'Lato'; font-style: normal; font-weight: 400; - font-display: swap; - src: url("../static/fonts/lato/lato-v16-latin-regular.eot"); /* IE9 Compat Modes */ - src: local("Lato Regular"), local("Lato-Regular"), - url("../static/fonts/lato/lato-v16-latin-regular.eot?#iefix") - format("embedded-opentype"), - /* IE6-IE8 */ url("../static/fonts/lato/lato-v16-latin-regular.woff2") - format("woff2"), + font-display: block; + src: url('../static/fonts/lato/lato-v16-latin-regular.eot'); /* IE9 Compat Modes */ + src: local('Lato Regular'), local('Lato-Regular'), + url('../static/fonts/lato/lato-v16-latin-regular.eot?#iefix') + format('embedded-opentype'), + /* IE6-IE8 */ url('../static/fonts/lato/lato-v16-latin-regular.woff2') + format('woff2'), /* Super Modern Browsers */ - url("../static/fonts/lato/lato-v16-latin-regular.woff") format("woff"), - /* Modern Browsers */ url("../static/fonts/lato/lato-v16-latin-regular.ttf") - format("truetype"), + url('../static/fonts/lato/lato-v16-latin-regular.woff') format('woff'), + /* Modern Browsers */ url('../static/fonts/lato/lato-v16-latin-regular.ttf') + format('truetype'), /* Safari, Android, iOS */ - url("../static/fonts/lato/lato-v16-latin-regular.svg#Lato") format("svg"); /* Legacy iOS */ + url('../static/fonts/lato/lato-v16-latin-regular.svg#Lato') format('svg'); /* Legacy iOS */ } /* lato-italic - latin */ @font-face { - font-family: "Lato"; + font-family: 'Lato'; font-style: italic; font-weight: 400; - font-display: swap; - src: url("../static/fonts/lato/lato-v16-latin-italic.eot"); /* IE9 Compat Modes */ - src: local("Lato Italic"), local("Lato-Italic"), - url("../static/fonts/lato/lato-v16-latin-italic.eot?#iefix") - format("embedded-opentype"), - /* IE6-IE8 */ url("../static/fonts/lato/lato-v16-latin-italic.woff2") - format("woff2"), + font-display: block; + src: url('../static/fonts/lato/lato-v16-latin-italic.eot'); /* IE9 Compat Modes */ + src: local('Lato Italic'), local('Lato-Italic'), + url('../static/fonts/lato/lato-v16-latin-italic.eot?#iefix') + format('embedded-opentype'), + /* IE6-IE8 */ url('../static/fonts/lato/lato-v16-latin-italic.woff2') + format('woff2'), /* Super Modern Browsers */ - url("../static/fonts/lato/lato-v16-latin-italic.woff") format("woff"), - /* Modern Browsers */ url("../static/fonts/lato/lato-v16-latin-italic.ttf") - format("truetype"), + url('../static/fonts/lato/lato-v16-latin-italic.woff') format('woff'), + /* Modern Browsers */ url('../static/fonts/lato/lato-v16-latin-italic.ttf') + format('truetype'), /* Safari, Android, iOS */ - url("../static/fonts/lato/lato-v16-latin-italic.svg#Lato") format("svg"); /* Legacy iOS */ + url('../static/fonts/lato/lato-v16-latin-italic.svg#Lato') format('svg'); /* Legacy iOS */ } /* montserrat-regular - latin */ @font-face { - font-family: "Montserrat"; + font-family: 'Montserrat'; font-style: normal; font-weight: 400; - font-display: swap; - src: url("../static/fonts/montserrat/montserrat-v14-latin-regular.eot"); /* IE9 Compat Modes */ - src: local("Montserrat Regular"), local("Montserrat-Regular"), - url("../static/fonts/montserrat/montserrat-v14-latin-regular.eot?#iefix") - format("embedded-opentype"), + font-display: block; + src: url('../static/fonts/montserrat/montserrat-v14-latin-regular.eot'); /* IE9 Compat Modes */ + src: local('Montserrat Regular'), local('Montserrat-Regular'), + url('../static/fonts/montserrat/montserrat-v14-latin-regular.eot?#iefix') + format('embedded-opentype'), /* IE6-IE8 */ - url("../static/fonts/montserrat/montserrat-v14-latin-regular.woff2") - format("woff2"), + url('../static/fonts/montserrat/montserrat-v14-latin-regular.woff2') + format('woff2'), /* Super Modern Browsers */ - url("../static/fonts/montserrat/montserrat-v14-latin-regular.woff") - format("woff"), + url('../static/fonts/montserrat/montserrat-v14-latin-regular.woff') + format('woff'), /* Modern Browsers */ - url("../static/fonts/montserrat/montserrat-v14-latin-regular.ttf") - format("truetype"), + url('../static/fonts/montserrat/montserrat-v14-latin-regular.ttf') + format('truetype'), /* Safari, Android, iOS */ - url("../static/fonts/montserrat/montserrat-v14-latin-regular.svg#Montserrat") - format("svg"); /* Legacy iOS */ + url('../static/fonts/montserrat/montserrat-v14-latin-regular.svg#Montserrat') + format('svg'); /* Legacy iOS */ } diff --git a/src/style/mixins.scss b/src/style/mixins.scss index 02c0857..fd2053b 100644 --- a/src/style/mixins.scss +++ b/src/style/mixins.scss @@ -37,6 +37,13 @@ justify-content: center; } +@mixin absolute-center() { + position: absolute; + left: 50%; + top: 50%; + transform: translateX(-50%) translateY(-50%); +} + @mixin card-base($vars) { text-align: center; padding: map_get($vars, $normal-margin); @@ -44,8 +51,8 @@ z-index: 1; @include on-large-screen { - transition: box-shadow map_get($vars, $long-transition-time); - + transition: box-shadow map_get($vars, $long-transition-time), + background-color map_get($vars, $long-transition-time); &:hover { box-shadow: map_get($vars, $shadow3), map_get($vars, $shadow2); } @@ -83,7 +90,8 @@ line-height: 1.6; } -@mixin insignificant-font() { +@mixin special-text-font($vars) { font: 400 1.1rem 'Lato', sans-serif; + color: map_get($vars, $special-text-color); font-style: italic; } diff --git a/src/style/vars.scss b/src/style/vars.scss index ff0ff2c..7e9512c 100644 --- a/src/style/vars.scss +++ b/src/style/vars.scss @@ -16,7 +16,9 @@ $important-card-text-color: id(); $inverse-text-color: id(); $card-color: id(); $important-card-color: id(); -$theme-switcher-color: id(); +$theme-switcher-background: id(); +$theme-switcher-foreground: id(); +$special-text-color: id(); $accent-color: id(); $scrollbar-color: id(); @@ -48,43 +50,12 @@ $universal-variables: ( $long-transition-time: 350ms, $line-width: 3px, $line-height: 18px, + $theme-switcher-foreground: #f7f78c, + $accent-color: #b7455e, + $important-card-color: #b7405a, $shadow3: 0 0 15px 4px rgba(0, 0, 0, 0.1), ); -$light-theme-variables: map_merge( - ( - $background: #ffffff, - $normal-text-color: #31343f, - $light-text-color: #7a7d8e, - $inverse-text-color: #ffffff, - $theme-switcher-color: #f7f78c, - $card-color: #ffffff, - $important-card-color: #aa4465, - $important-card-text-color: #ffffff, - $accent-color: #aa4465, - $scrollbar-color: #ffd6d6, - ), - $universal-variables -); - -$dark-theme-variables: map_merge( - ( - $background: #242638, - $normal-text-color: #ffffff, - $light-text-color: #fff9e0, - $inverse-text-color: #242638, - $theme-switcher-color: #ffff92, - $card-color: #263551, - $important-card-text-color: #fff9e0, - $accent-color: #f7f78c, - $important-card-color: #263551, - $scrollbar-color: #ffd6d6, - $shadow1: 0 0 10px 2px rgba(0, 0, 0, 0.175), - $shadow2: 0 0 1px rgba(0, 0, 0, 0.4), - ), - $universal-variables -); - $large-screen-variables: map_merge( ( $border-radius: 15px, @@ -112,3 +83,33 @@ $small-screen-variables: map_merge( ), $universal-variables ); + +$light-theme-variables: map_merge( + ( + $background: #ffffff, + $normal-text-color: #31343f, + $light-text-color: #7a7d8e, + $inverse-text-color: #ffffff, + $card-color: #ffffff, + $important-card-text-color: #ffffff, + $special-text-color: map_get($universal-variables, $accent-color), + $scrollbar-color: #ffd6d6, + ), + $universal-variables +); + +$dark-theme-variables: map_merge( + ( + $background: #242638, + $normal-text-color: #ffffff, + $light-text-color: #fff9e0, + $inverse-text-color: #242638, + $card-color: #263551, + $special-text-color: #ffffff, + $important-card-text-color: #fff9e0, + $scrollbar-color: #ffd6d6, + $shadow1: 0 0 10px 2px rgba(0, 0, 0, 0.175), + $shadow2: 0 0 1px rgba(0, 0, 0, 0.4), + ), + $universal-variables +); diff --git a/src/styles.scss b/src/styles.scss index b8450c8..67ae6bd 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -6,15 +6,23 @@ @include responsive() using ($vars) { & { background-color: map_get($vars, $background); + transition: background-color map_get($vars, $long-transition-time); + height: 100%; @include on-small-screen { font-size: 0.8em; } - } - svg { - fill: hotpink; + &[animations='off'] { + &, + *, + *::before, + *::after { + transition: none !important; + animation: none !important; + } + } } body { @@ -28,11 +36,16 @@ overflow: hidden; } - * { + *, + *::before, + *::after { margin: 0; padding: 0; box-sizing: border-box; color: map_get($vars, $normal-text-color); + transition: background-color map_get($vars, $long-transition-time), + color map_get($vars, $long-transition-time); + hyphens: auto; :focus { diff --git a/webpack.config.js b/webpack.config.js index 6e740e5..6e3d957 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -16,7 +16,7 @@ module.exports = { }, devServer: { host: '0.0.0.0', - //disableHostCheck: true, + disableHostCheck: true, }, optimization: { minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],