Add dark mode
This commit is contained in:
parent
48a55a4a97
commit
073f087e52
40 changed files with 864 additions and 531 deletions
1
.idea/inspectionProfiles/Project_Default.xml
generated
1
.idea/inspectionProfiles/Project_Default.xml
generated
|
|
@ -2,5 +2,6 @@
|
|||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="CssInvalidAtRule" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="JSBitwiseOperatorUsage" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
||||
56
.idea/workspace.xml
generated
56
.idea/workspace.xml
generated
|
|
@ -2,54 +2,43 @@
|
|||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="8edc47ab-1265-4111-9771-536b24cc9310" name="Default Changelist" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/.idea/codeStyles/Project.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.idea/codeStyles/codeStyleConfig.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/framework/cache.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/framework/container-page.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/framework/event-broadcaster.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/framework/primitives/implementations/anchor.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/framework/primitives/implementations/image.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/framework/primitives/implementations/text.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/framework/primitives/implementations/video.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/framework/primitives/primitive.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/framework/primitives/primitives.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/dictionaries/andras.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dictionaries/andras.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/framework/helper/dark-mode.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/framework/polyfills.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/page/content/content.html.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/page/theme-switcher/theme-switcher.html.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/page/theme-switcher/theme-switcher.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/page/theme-switcher/theme-switcher.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/inspectionProfiles/Project_Default.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/inspectionProfiles/Project_Default.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.prettierrc" beforeDir="false" afterPath="$PROJECT_DIR$/.prettierrc" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/framework/helper/color-mixer.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/framework/helper/mix-colors.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/framework/helper/create-element.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/framework/helper/create-element.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/framework/helper/get-height.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/framework/helper/get-height.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/framework/helper/last.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/framework/helper/last.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/framework/container-page.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/framework/container-page.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/framework/helper/mix-colors.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/framework/helper/mix-colors.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/framework/helper/random.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/framework/helper/random.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/framework/helper/range.ts" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/framework/page-element.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/framework/page-element.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/framework/page.ts" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/model/content.ts" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/model/portfolio.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/model/portfolio.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/about/about.html.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/about/about.html.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/framework/page-event.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/framework/page-event.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/framework/primitives/primitives.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/framework/primitives/primitives.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/src/index.html" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/about/about.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/about/about.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/about/about.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/about/about.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/background/animation.ts" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/background/background.html.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/background/background.html.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/background/background.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/background/background.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/background/background.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/background/background.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/background/blob.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/background/blob.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/background/vec2.ts" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/background/vec3.ts" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/content/content.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/content/content.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/content/content.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/content/content.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/footer/footer.html.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/footer/footer.html.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/footer/footer.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/footer/footer.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/footer/footer.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/footer/footer.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/image-viewer/image-viewer.html.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/image-viewer/image-viewer.html.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/image-viewer/image-viewer.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/image-viewer/image-viewer.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/image-viewer/image-viewer.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/image-viewer/image-viewer.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/index.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/timeline/timeline-element/timeline-element.html.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/timeline/timeline-element/timeline-element.html.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/timeline/timeline-element/timeline-element.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/timeline/timeline-element/timeline-element.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/timeline/timeline-element/timeline-element.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/timeline/timeline-element/timeline-element.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/timeline/timeline.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/timeline/timeline.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/portfolio.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/portfolio.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/page/timeline/timeline.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/page/timeline/timeline.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/static/icons/at.svg" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/static/icons/cv.svg" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/style/a.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/style/a.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/style/mixins.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/style/mixins.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/style/vars.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/style/vars.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/styles.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/styles.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/webpack.config.js" beforeDir="false" afterPath="$PROJECT_DIR$/webpack.config.js" afterDir="false" />
|
||||
|
|
@ -82,7 +71,7 @@
|
|||
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="ignore_missing_gitignore" value="true" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$/src/page/background" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$/../../life-towers" />
|
||||
<property name="node.js.detected.package.stylelint" value="true" />
|
||||
<property name="node.js.path.for.package.stylelint" value="project" />
|
||||
<property name="node.js.selected.package.stylelint" value="" />
|
||||
|
|
@ -156,7 +145,10 @@
|
|||
<workItem from="1578128641780" duration="1136000" />
|
||||
<workItem from="1578140662891" duration="4319000" />
|
||||
<workItem from="1578213355277" duration="599000" />
|
||||
<workItem from="1578327147961" duration="14056000" />
|
||||
<workItem from="1578327147961" duration="16067000" />
|
||||
<workItem from="1578383579118" duration="5883000" />
|
||||
<workItem from="1578391423465" duration="34000" />
|
||||
<workItem from="1578392333248" duration="20581000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -1,6 +1,12 @@
|
|||
# Timeline
|
||||
|
||||
[Click for live version](https://schmelczer.dev)
|
||||
|
||||
An easily configurable portfolio.
|
||||
|
||||
> [Click for live version](https://schmelczer.dev)
|
||||
|
||||
## Configuration
|
||||
- The actual content is in [portfolio.ts](src/portfolio.ts).
|
||||
- The assets referenced by this file should be located in [src/static](src/static).
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
"html-webpack-plugin": "^3.2.0",
|
||||
"image-webpack-loader": "^6.0.0",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"sass": "^1.24.2",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.3",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"prettier": "^1.19.1",
|
||||
|
|
@ -56,5 +56,8 @@
|
|||
"webpack": "^4.41.4",
|
||||
"webpack-cli": "^3.3.10",
|
||||
"webpack-dev-server": "^3.10.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"sass": "latest"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,16 +3,18 @@ import { PageEventType } from './page-event';
|
|||
|
||||
export class ContainerPage extends PageElement {
|
||||
public constructor(rootElement: HTMLElement, children: Array<PageElement>) {
|
||||
children.forEach(e => rootElement.appendChild(e.element));
|
||||
children
|
||||
.filter(e => e.element)
|
||||
.forEach(e => rootElement.appendChild(e.element));
|
||||
super(rootElement, children);
|
||||
}
|
||||
|
||||
public setAsMain() {
|
||||
this.broadcastEvent({ type: PageEventType.onLoad }, this);
|
||||
|
||||
this.broadcastEvent(
|
||||
{ type: PageEventType.eventBroadcasterChanged, data: this },
|
||||
this
|
||||
);
|
||||
|
||||
this.broadcastEvent({ type: PageEventType.onLoad }, this);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
9
src/framework/helper/dark-mode.ts
Normal file
9
src/framework/helper/dark-mode.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
export const isSystemLevelDarkModeEnabled = (): boolean =>
|
||||
window.matchMedia &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
export const turnOnDarkMode = () =>
|
||||
document.body.parentElement.setAttribute('theme', 'dark');
|
||||
|
||||
export const turnOnLightMode = () =>
|
||||
document.body.parentElement.setAttribute('theme', 'light');
|
||||
|
|
@ -20,16 +20,12 @@ export const mixColors = (
|
|||
|
||||
const hexToRGB = (hex: hex): rgb => {
|
||||
const [r1, r2, g1, g2, b1, b2] = normalizeHex(hex);
|
||||
return [
|
||||
Number.parseInt(r1 + r2, 16),
|
||||
Number.parseInt(g1 + g2, 16),
|
||||
Number.parseInt(b1 + b2, 16),
|
||||
];
|
||||
return [parseInt(r1 + r2, 16), parseInt(g1 + g2, 16), parseInt(b1 + b2, 16)];
|
||||
};
|
||||
|
||||
const normalizeHex = (hex: hex): hex => {
|
||||
hex = hex.trim();
|
||||
if (hex.startsWith('#')) {
|
||||
if (hex[0] === '#') {
|
||||
hex = hex.substr(1);
|
||||
}
|
||||
return hex;
|
||||
|
|
@ -38,4 +34,4 @@ const normalizeHex = (hex: hex): hex => {
|
|||
const mix = (a: number, b: number, q: number): number => a * q + b * (1 - q);
|
||||
|
||||
const rgbToHex = (rgb: rgb): hex =>
|
||||
'#' + rgb.map(n => Math.round(n).toString(16)).join('');
|
||||
'#' + rgb.map(n => (n < 16 ? '0' : '') + Math.round(n).toString(16)).join('');
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { addImul } from '../polyfills';
|
||||
|
||||
export class Random {
|
||||
public constructor(private seed: number) {}
|
||||
public constructor(private seed: number) {
|
||||
addImul();
|
||||
}
|
||||
|
||||
public get next(): number {
|
||||
// result is in [0, 1)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export abstract class PageElement implements EventBroadcaster {
|
|||
protected eventBroadcaster: EventBroadcaster;
|
||||
|
||||
protected constructor(
|
||||
private readonly rootElement: HTMLElement,
|
||||
private readonly rootElement?: HTMLElement,
|
||||
private readonly children: Array<PageElement> = []
|
||||
) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,5 +6,6 @@ export class PageEvent {
|
|||
export enum PageEventType {
|
||||
onLoad,
|
||||
onBodyDimensionsChanged,
|
||||
eventBroadcasterChanged
|
||||
eventBroadcasterChanged,
|
||||
pageThemeChanged,
|
||||
}
|
||||
|
|
|
|||
17
src/framework/polyfills.ts
Normal file
17
src/framework/polyfills.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
export const addImul = () => {
|
||||
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
|
||||
// automatically handled for our convenience:
|
||||
// 1. 0x003fffff /*opA & 0x000fffff*/ * 0x7fffffff /*opB*/ = 0x1fffff7fc00001
|
||||
// 0x1fffff7fc00001 < Number.MAX_SAFE_INTEGER /*0x1fffffffffffff*/
|
||||
let result = (opA & 0x003fffff) * opB;
|
||||
// 2. We can remove an integer coercion from the statement above because:
|
||||
// 0x1fffff7fc00001 + 0xffc00000 = 0x1fffffff800001
|
||||
// 0x1fffffff800001 < Number.MAX_SAFE_INTEGER /*0x1fffffffffffff*/
|
||||
if (opA & 0xffc00000 /*!== 0*/) result += ((opA & 0xffc00000) * opB) | 0;
|
||||
return result | 0;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,25 +1,27 @@
|
|||
@import '../../style/vars';
|
||||
@import '../../style/mixins';
|
||||
|
||||
.figure-container {
|
||||
font-size: 0;
|
||||
box-shadow: inset $shadow1, inset $shadow2;
|
||||
pointer-events: none;
|
||||
cursor: pointer;
|
||||
@include responsive() using ($vars) {
|
||||
.figure-container {
|
||||
font-size: 0;
|
||||
box-shadow: inset map_get($vars, $shadow1), inset map_get($vars, $shadow2);
|
||||
pointer-events: none;
|
||||
cursor: pointer;
|
||||
|
||||
* {
|
||||
pointer-events: all;
|
||||
position: relative;
|
||||
z-index: -2;
|
||||
* {
|
||||
pointer-events: all;
|
||||
position: relative;
|
||||
z-index: -2;
|
||||
}
|
||||
}
|
||||
|
||||
.primitive-text,
|
||||
.primitive-anchor,
|
||||
.figure-container {
|
||||
margin-top: map_get($vars, $line-height);
|
||||
}
|
||||
|
||||
.primitive-text {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.primitive-text,
|
||||
.primitive-anchor,
|
||||
.figure-container {
|
||||
margin-top: $line-height;
|
||||
}
|
||||
|
||||
.primitive-text {
|
||||
text-align: left;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
<title>Portfolio - András Schmelczer</title>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="main"> <!-- for IE11 -->
|
||||
<noscript><h1>Javascript is required for this website.</h1></noscript>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,64 +1,75 @@
|
|||
@import '../../style/mixins';
|
||||
@import '../../style/vars';
|
||||
|
||||
#about {
|
||||
@include important-card();
|
||||
@include responsive() using ($vars) {
|
||||
#about {
|
||||
@include card-base($vars);
|
||||
|
||||
$img-size: 125px;
|
||||
background-color: map_get($vars, $important-card-color);
|
||||
|
||||
h1,
|
||||
img,
|
||||
.placeholder,
|
||||
& {
|
||||
@include title-font();
|
||||
}
|
||||
|
||||
img {
|
||||
@include square($img-size);
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
p {
|
||||
@include main-font();
|
||||
text-align: justify;
|
||||
margin-top: $small-margin;
|
||||
}
|
||||
|
||||
h1 {
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-width) {
|
||||
h1 {
|
||||
margin-top: $small-margin;
|
||||
* {
|
||||
color: map_get($vars, $important-card-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $breakpoint-width) {
|
||||
$img-size: 190px;
|
||||
$img-size: 125px;
|
||||
|
||||
width: $body-width;
|
||||
margin: calc(#{$normal-margin} + #{$img-size} * 1 / 3) auto 0 auto;
|
||||
position: relative;
|
||||
border-radius: $border-radius;
|
||||
h1,
|
||||
img,
|
||||
.placeholder,
|
||||
& {
|
||||
@include title-font();
|
||||
}
|
||||
|
||||
img {
|
||||
@include square($img-size);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
transform: translateY(-$img-size * 1/3) translateX(-$img-size * 1/3);
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
@include square(calc(#{$img-size} * 2 / 3 - #{$normal-margin}));
|
||||
box-sizing: content-box;
|
||||
float: left;
|
||||
margin: 0 0.75ex 0.75ex 0;
|
||||
p {
|
||||
@include main-font();
|
||||
text-align: justify;
|
||||
margin-top: map_get($vars, $small-margin);
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: left;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
@include on-small-screen {
|
||||
h1 {
|
||||
margin-top: map_get($vars, $small-margin);
|
||||
}
|
||||
}
|
||||
|
||||
@include on-large-screen {
|
||||
$img-size: 190px;
|
||||
|
||||
width: map_get($vars, $body-width);
|
||||
margin: calc(#{map_get($vars, $normal-margin)} + #{$img-size} * 1 / 3)
|
||||
auto 0 auto;
|
||||
position: relative;
|
||||
border-radius: map_get($vars, $border-radius);
|
||||
|
||||
img {
|
||||
@include square($img-size);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
transform: translateY(-$img-size * 1/3) translateX(-$img-size * 1/3);
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
@include square(
|
||||
calc(#{$img-size} * 2 / 3 - #{map_get($vars, $normal-margin)})
|
||||
);
|
||||
box-sizing: content-box;
|
||||
float: left;
|
||||
margin: 0 0.75ex 0.75ex 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,13 @@ import { PageElement } from '../../framework/page-element';
|
|||
import { generate } from './about.html';
|
||||
import { createElement } from '../../framework/helper/create-element';
|
||||
import { ContainerPage } from '../../framework/container-page';
|
||||
import { PageThemeSwitcher } from '../theme-switcher/theme-switcher';
|
||||
|
||||
export class PageHeader extends ContainerPage {
|
||||
public constructor(header: Header) {
|
||||
super(createElement(generate(header)), [new PageContent(header.about)]);
|
||||
super(createElement(generate(header)), [
|
||||
new PageContent(header.about),
|
||||
new PageThemeSwitcher(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,5 @@ import { html } from '../../model/misc';
|
|||
import './background.scss';
|
||||
|
||||
export const generate = (): html => `
|
||||
<section id="background-container">
|
||||
<section id="background"></section>
|
||||
</section>
|
||||
<div class="background-element"></div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,43 +1,39 @@
|
|||
@import '../../style/vars';
|
||||
@import '../../style/mixins';
|
||||
|
||||
#background-container {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
@include responsive() using ($vars) {
|
||||
div.background-element {
|
||||
position: -webkit-sticky;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-radius: 100px;
|
||||
width: 140px;
|
||||
|
||||
z-index: -1;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
perspective: 5px;
|
||||
perspective-origin: center center;
|
||||
overflow: hidden;
|
||||
overflow: visible !important; // IE11 fix for disappearing elements
|
||||
|
||||
#background {
|
||||
overflow: hidden;
|
||||
will-change: width, height;
|
||||
transition: height $long-transition-time, width $long-transition-time;
|
||||
transform-style: flat;
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
& {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
position: -webkit-sticky;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-radius: 100px;
|
||||
width: 140px;
|
||||
@media print {
|
||||
& {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
transition: transform $long-transition-time, opacity $long-transition-time;
|
||||
will-change: transform, opacity;
|
||||
animation: fade-in 1s linear;
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
transition: transform map_get($vars, $long-transition-time),
|
||||
opacity map_get($vars, $long-transition-time);
|
||||
will-change: transform, opacity;
|
||||
animation: fade-in 1s linear;
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { PageElement } from '../../framework/page-element';
|
||||
import { PageEvent, PageEventType } from '../../framework/page-event';
|
||||
import { createElement } from '../../framework/helper/create-element';
|
||||
import { Blob } from './blob';
|
||||
import { generate } from './background.html';
|
||||
import { Random } from '../../framework/helper/random';
|
||||
import { getHeight } from '../../framework/helper/get-height';
|
||||
import { sum } from '../../framework/helper/sum';
|
||||
|
|
@ -12,7 +10,7 @@ export class PageBackground extends PageElement {
|
|||
private readonly blobSpacing = 350;
|
||||
|
||||
public constructor(private start: PageElement, private end: PageElement) {
|
||||
super(createElement(generate()));
|
||||
super();
|
||||
Blob.initialize(10, 30, 5);
|
||||
}
|
||||
|
||||
|
|
@ -21,37 +19,33 @@ export class PageBackground extends PageElement {
|
|||
this.bindListeners(parent);
|
||||
} else if (event.type === PageEventType.onBodyDimensionsChanged) {
|
||||
this.resize(parent, event.data?.deltaHeight);
|
||||
} else if (event.type === PageEventType.pageThemeChanged) {
|
||||
Blob.changeTheme(event.data);
|
||||
this.blobs.forEach(b => b.decideColor());
|
||||
}
|
||||
}
|
||||
|
||||
private bindListeners(parent: PageElement) {
|
||||
window.addEventListener('resize', () => this.resize(parent));
|
||||
window.addEventListener('load', () => this.resize(parent));
|
||||
parent.element.addEventListener(
|
||||
'scroll',
|
||||
() => (this.element.scrollTop = parent.element.scrollTop)
|
||||
);
|
||||
}
|
||||
|
||||
private resize(parent: PageElement, heightChange?: number) {
|
||||
const siblings: Array<HTMLElement> = this.getSiblings(parent);
|
||||
const siblings: Array<HTMLElement> = PageBackground.getSiblings(parent);
|
||||
|
||||
const width = parent.element.clientWidth;
|
||||
const width = document.body.clientWidth;
|
||||
let height = sum(siblings.map(getHeight));
|
||||
if (heightChange) {
|
||||
height += heightChange;
|
||||
}
|
||||
|
||||
this.query('#background').style.width = `${width}px`;
|
||||
this.query('#background').style.height = `${height}px`;
|
||||
|
||||
const requiredBlobCount = Math.round(
|
||||
(width * height) / this.blobSpacing ** 2
|
||||
);
|
||||
|
||||
while (requiredBlobCount > this.blobs.length) {
|
||||
const blob = new Blob();
|
||||
this.query('#background').appendChild(blob.htmlElement);
|
||||
parent.element.appendChild(blob.htmlElement);
|
||||
this.blobs.push(blob);
|
||||
}
|
||||
|
||||
|
|
@ -74,9 +68,9 @@ export class PageBackground extends PageElement {
|
|||
});
|
||||
}
|
||||
|
||||
private getSiblings(parent: PageElement): Array<HTMLElement> {
|
||||
private static getSiblings(parent: PageElement): Array<HTMLElement> {
|
||||
return Array.prototype.slice
|
||||
.call(parent.element.children)
|
||||
.filter(e => e !== this.element);
|
||||
.filter((e: HTMLElement) => !e.classList.contains('background-element'));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
import { mixColors } from '../../framework/helper/mix-colors';
|
||||
import { createElement } from '../../framework/helper/create-element';
|
||||
import { Random } from '../../framework/helper/random';
|
||||
import { generate } from './background.html';
|
||||
|
||||
export class Blob {
|
||||
private static readonly creatorRandom = new Random(44);
|
||||
private static readonly colors = ['#fff9e0', '#ffd6d6'];
|
||||
private static colorPickerRandom = new Random(132);
|
||||
private static readonly lightColors = ['#fff9e0', '#ffd6d6'];
|
||||
private static readonly darkColors = ['#2C477A'];
|
||||
private static isDarkThemed = false;
|
||||
private static zMin: number;
|
||||
private static zMax: number;
|
||||
private static perspective: number;
|
||||
|
|
@ -14,25 +18,36 @@ export class Blob {
|
|||
Blob.perspective = perspective;
|
||||
}
|
||||
|
||||
public static changeTheme(isDarkThemed: boolean) {
|
||||
Blob.colorPickerRandom = new Random(132);
|
||||
Blob.isDarkThemed = isDarkThemed;
|
||||
}
|
||||
|
||||
private readonly z = Blob.creatorRandom.randomInInterval(
|
||||
Blob.zMin,
|
||||
Blob.zMax
|
||||
);
|
||||
|
||||
private readonly element: HTMLElement = createElement('<div></div>');
|
||||
private readonly element: HTMLElement = createElement(generate());
|
||||
constructor() {
|
||||
this.element.style.backgroundColor = mixColors(
|
||||
'#ffffff',
|
||||
Blob.creatorRandom.choose(Blob.colors),
|
||||
(this.z - Blob.zMin) / (Blob.zMax - Blob.zMin)
|
||||
);
|
||||
this.element.style.zIndex = (-this.z).toString();
|
||||
this.decideColor();
|
||||
this.element.style.zIndex = Math.round(-this.z).toString();
|
||||
this.element.style.height = `${Blob.creatorRandom.randomInInterval(
|
||||
160,
|
||||
740
|
||||
)}px`;
|
||||
}
|
||||
|
||||
public decideColor() {
|
||||
this.element.style.backgroundColor = mixColors(
|
||||
Blob.isDarkThemed ? '#242638 ' : '#ffffff',
|
||||
Blob.colorPickerRandom.choose(
|
||||
Blob.isDarkThemed ? Blob.darkColors : Blob.lightColors
|
||||
),
|
||||
(this.z - Blob.zMin) / (Blob.zMax - Blob.zMin)
|
||||
);
|
||||
}
|
||||
|
||||
get htmlElement(): HTMLElement {
|
||||
return this.element;
|
||||
}
|
||||
|
|
|
|||
10
src/page/content/content.html.ts
Normal file
10
src/page/content/content.html.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { Content } from '../../model/portfolio';
|
||||
import { html } from '../../model/misc';
|
||||
|
||||
import './content.scss';
|
||||
|
||||
export const generate = (content: Content): html => `
|
||||
<div class="content">
|
||||
${content.map(element => element.toHTML()).join('\n')}
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
@import '../../style/vars';
|
||||
@import '../../style/mixins';
|
||||
|
||||
.content {
|
||||
margin-top: $small-margin;
|
||||
@include responsive() using ($vars) {
|
||||
.content {
|
||||
margin-top: map_get($vars, $small-margin);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,10 @@
|
|||
import './content.scss';
|
||||
import { PageElement } from '../../framework/page-element';
|
||||
import { createElement } from '../../framework/helper/create-element';
|
||||
import { Content } from '../../model/portfolio';
|
||||
import { generate } from './content.html';
|
||||
|
||||
export class PageContent extends PageElement {
|
||||
public constructor(content: Content) {
|
||||
super(
|
||||
createElement(`
|
||||
<div class="content">
|
||||
${content.map(element => element.toHTML()).join('\n')}
|
||||
</div>
|
||||
`)
|
||||
);
|
||||
super(createElement(generate(content)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import { Footer } from '../../model/portfolio';
|
||||
import { html } from '../../model/misc';
|
||||
import emailIcon from '../../static/icons/at.svg';
|
||||
import cvIcon from '../../static/icons/cv.svg';
|
||||
|
||||
import './footer.scss';
|
||||
|
||||
|
|
@ -20,13 +18,23 @@ export const generate = ({
|
|||
.map(
|
||||
cv =>
|
||||
`<li>
|
||||
<img src="${cvIcon}" alt="CV" class="no-open" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 512.002 512.002" style="enable-background:new 0 0 512.002 512.002;" xml:space="preserve" width="512px" height="512px" class="">
|
||||
<path d="M394.667,42.667h-128c-11.782,0-21.333,9.551-21.333,21.333v128c0,11.782,9.551,21.333,21.333,21.333h128 c11.782,0,21.333-9.551,21.333-21.333V64C416,52.218,406.449,42.667,394.667,42.667z M290.347,192 c7.707-22.268,32.007-34.072,54.275-26.365c12.366,4.28,22.085,13.999,26.365,26.365H290.347z M309.333,117.333 c0-11.782,9.551-21.333,21.333-21.333C342.449,96,352,105.551,352,117.333c0,11.782-9.551,21.333-21.333,21.333 C318.885,138.667,309.333,129.116,309.333,117.333z M394.667,192h-1.387c-4.028-18.843-16.331-34.868-33.493-43.627 c17.25-16.053,18.221-43.051,2.167-60.301c-16.053-17.25-43.051-18.221-60.301-2.167c-17.25,16.053-18.221,43.051-2.167,60.301 c0.696,0.748,1.419,1.471,2.167,2.167c-17.203,8.734-29.548,24.763-33.6,43.627h-1.387V64h128V192z" class="active-path" fill="#31343F"/>
|
||||
<path d="M432.429,0.002C432.357,0.001,432.285,0,432.214,0h-282.88c-2.835-0.016-5.56,1.097-7.573,3.093L56.427,88.427 c-1.997,2.013-3.11,4.738-3.093,7.573v389.547c0.058,14.586,11.868,26.395,26.453,26.453h352 c14.668,0.177,26.701-11.57,26.878-26.238c0.001-0.072,0.001-0.144,0.002-0.215V26.88 C458.844,12.213,447.097,0.179,432.429,0.002z M437.334,485.547c0,2.828-2.292,5.12-5.12,5.12h-352 c-2.818,0.236-5.293-1.858-5.529-4.675c-0.012-0.148-0.018-0.296-0.018-0.445V100.374l64-64v44.16 c-0.057,2.627-2.173,4.743-4.8,4.8h-27.2v21.333h32c12.352-2.351,21.299-13.133,21.333-25.707V21.333h272.213 c2.828,0,5.12,2.292,5.12,5.12V485.547z" class="active-path" fill="#31343F"/>
|
||||
<rect x="192" y="256" width="170.667" height="21.333" class="active-path" fill="#31343F"/>
|
||||
<rect x="149.333" y="309.333" width="213.333" height="21.333" class="active-path" fill="#31343F"/>
|
||||
<rect x="149.333" y="362.667" width="213.333" height="21.333" class="active-path" fill="#31343F"/>
|
||||
<rect x="149.333" y="416" width="170.667" height="21.333" class="active-path" fill="#31343F"/>
|
||||
<rect x="341.334" y="416" width="21.333" height="21.333" class="active-path" fill="#31343F"/>
|
||||
</svg>
|
||||
<a id="cv" href="${cv.url}" target="_blank">${cv.name}</a>
|
||||
</li>`
|
||||
)
|
||||
.join('\n')}
|
||||
<li>
|
||||
<img src="${emailIcon}" alt="email" class="no-open"/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="512px" viewBox="0 0 512.00018 512" width="512px">
|
||||
<path d="m401.738281 442.710938c6.046875 9.242187 3.453125 21.636718-5.792969 27.683593-41.585937 27.195313-89.9375 41.582031-139.824218 41.605469-.039063 0-.082032 0-.121094 0-68.097656.003906-132.101562-26.234375-180.253906-73.890625-48.183594-47.683594-75.078125-111.46875-75.7343752-179.597656-.6601568-68.558594 25.4453122-133.210938 73.5039062-182.054688 48.066406-48.851562 112.265625-76 180.773437-76.45312475.5625 0 1.136719-.00390625 1.699219-.00390625 53.46875 0 104.816407 16.546875 148.613281 47.90625 43.277344 30.988281 75.519532 73.652344 93.238282 123.378906 3.707031 10.40625-1.722656 21.847656-12.125 25.554688-10.40625 3.707031-21.84375-1.722656-25.554688-12.125-30.898437-86.707032-112.84375-144.714844-204.195312-144.714844-.476563 0-.9375 0-1.414063.003906-57.796875.378906-111.964843 23.289063-152.523437 64.507813-40.546875 41.210937-62.570313 95.765625-62.015625 153.613281 1.132812 117.949219 98.019531 213.875 215.992187 213.875h.097656c42.097657-.019531 82.886719-12.148438 117.953126-35.082031 9.242187-6.046875 21.636718-3.453125 27.683593 5.792969zm110.261719-165.710938c0 50.179688-40.820312 91-91 91-32.0625 0-60.304688-16.667969-76.519531-41.796875-20.71875 26.058594-52.679688 42.796875-88.480469 42.796875-62.308594 0-113-50.691406-113-113s50.691406-113 113-113c28.277344 0 54.160156 10.441406 74 27.667969v-3.535157c0-11.042968 8.953125-20 20-20s20 8.957032 20 20v109.867188c0 28.121094 22.878906 51 51 51s51-22.878906 51-51c0-11.046875 8.953125-20 20-20s20 8.953125 20 20zm-183-21c0-40.25-32.746094-73-73-73-40.25 0-73 32.75-73 73 0 40.253906 32.75 73 73 73 40.253906 0 73-32.746094 73-73zm0 0" class="active-path" fill="#31343F"/>
|
||||
</svg>
|
||||
<a id="email" href="mailto:${email}">${email}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -1,51 +1,58 @@
|
|||
@import '../../style/mixins';
|
||||
@import '../../style/vars';
|
||||
|
||||
footer#page-footer {
|
||||
text-align: center;
|
||||
@include responsive() using ($vars) {
|
||||
footer#page-footer {
|
||||
text-align: center;
|
||||
|
||||
margin: $large-margin auto 0 auto;
|
||||
width: 100%;
|
||||
margin: map_get($vars, $large-margin) auto 0 auto;
|
||||
width: 100%;
|
||||
|
||||
h2 {
|
||||
@include title-font();
|
||||
}
|
||||
h2 {
|
||||
@include title-font();
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
display: inline-block;
|
||||
margin-top: $normal-margin;
|
||||
text-align: left;
|
||||
ul {
|
||||
list-style: none;
|
||||
display: inline-block;
|
||||
margin-top: map_get($vars, $normal-margin);
|
||||
text-align: left;
|
||||
|
||||
li {
|
||||
@include center-children();
|
||||
justify-content: flex-start;
|
||||
li {
|
||||
@include center-children();
|
||||
justify-content: flex-start;
|
||||
|
||||
&:not(:first-child) {
|
||||
padding-top: $line-height;
|
||||
}
|
||||
&:not(:first-child) {
|
||||
padding-top: map_get($vars, $line-height);
|
||||
}
|
||||
|
||||
img {
|
||||
@include max-square($icon-size);
|
||||
margin-right: $small-margin;
|
||||
}
|
||||
img,
|
||||
svg {
|
||||
@include max-square(map_get($vars, $icon-size));
|
||||
* {
|
||||
fill: map_get($vars, $normal-text-color);
|
||||
}
|
||||
margin-right: map_get($vars, $small-margin);
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 1.4rem;
|
||||
a {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aside.other {
|
||||
@include center-children();
|
||||
flex-direction: column;
|
||||
margin: $large-margin auto $line-height auto;
|
||||
width: $body-width;
|
||||
aside.other {
|
||||
@include center-children();
|
||||
flex-direction: column;
|
||||
margin: map_get($vars, $large-margin) auto map_get($vars, $line-height)
|
||||
auto;
|
||||
width: map_get($vars, $body-width);
|
||||
|
||||
h6 {
|
||||
@include insignificant-font();
|
||||
display: inline;
|
||||
opacity: 0.75;
|
||||
h6 {
|
||||
@include insignificant-font();
|
||||
display: inline;
|
||||
opacity: 0.75;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { html } from "../../model/misc";
|
||||
import cancel from "../../static/icons/cancel.svg";
|
||||
import "./image-viewer.scss";
|
||||
import { html } from '../../model/misc';
|
||||
import cancel from '../../static/icons/cancel.svg';
|
||||
|
||||
import './image-viewer.scss';
|
||||
|
||||
export const generate = (): html => `
|
||||
<section id="image-viewer">
|
||||
<img id="photo" alt="currently opened photo"/>
|
||||
<div id="container"></div>
|
||||
<img id="cancel" src="${cancel}" alt="cancel"/>
|
||||
</section>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,30 +1,41 @@
|
|||
@import '../../style/vars';
|
||||
@import '../../style/mixins';
|
||||
|
||||
#image-viewer {
|
||||
@include center-children();
|
||||
display: none;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
margin: 0;
|
||||
z-index: 2;
|
||||
background-color: rgba(0, 0, 0, 0.75);
|
||||
|
||||
#photo {
|
||||
max-width: 80vw;
|
||||
max-height: 80vh;
|
||||
}
|
||||
|
||||
#cancel {
|
||||
@include square($icon-size);
|
||||
position: absolute;
|
||||
box-sizing: content-box;
|
||||
padding: $normal-margin;
|
||||
right: 0;
|
||||
@include responsive() using ($vars) {
|
||||
#image-viewer {
|
||||
@include center-children();
|
||||
display: none;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
z-index: 2;
|
||||
background-color: rgba(0, 0, 0, 0.85);
|
||||
|
||||
@include on-large-screen {
|
||||
#container > * {
|
||||
max-width: 80vw;
|
||||
max-height: 80vh;
|
||||
}
|
||||
}
|
||||
|
||||
@include on-small-screen {
|
||||
#container > * {
|
||||
max-width: 95vw;
|
||||
max-height: 95vh;
|
||||
}
|
||||
}
|
||||
|
||||
#cancel {
|
||||
@include square(map_get($vars, $icon-size));
|
||||
position: absolute;
|
||||
box-sizing: content-box;
|
||||
padding: map_get($vars, $normal-margin);
|
||||
right: 0;
|
||||
top: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,8 @@ import { createElement } from '../../framework/helper/create-element';
|
|||
|
||||
export class PageImageViewer extends PageElement {
|
||||
public constructor() {
|
||||
const root = createElement(generate());
|
||||
root.onclick = () => PageImageViewer.hide(root);
|
||||
super(root);
|
||||
super(createElement(generate()));
|
||||
this.element.onclick = () => PageImageViewer.hide(this.element);
|
||||
}
|
||||
|
||||
protected handleEvent(event: PageEvent, parent: PageElement) {
|
||||
|
|
@ -18,25 +17,26 @@ export class PageImageViewer extends PageElement {
|
|||
|
||||
document.body.addEventListener('keydown', this.handleKeydown.bind(this));
|
||||
|
||||
const images = Array.prototype.slice.call(
|
||||
parent.element.querySelectorAll('img')
|
||||
const media = Array.prototype.slice.call(
|
||||
parent.element.querySelectorAll('img, video')
|
||||
);
|
||||
images
|
||||
|
||||
media
|
||||
.filter(
|
||||
(img: HTMLImageElement) =>
|
||||
img.parentElement !== this.element &&
|
||||
!img.classList.contains('no-open')
|
||||
(e: HTMLElement) =>
|
||||
e.parentElement !== this.element && !e.classList.contains('no-open')
|
||||
)
|
||||
.forEach(
|
||||
(img: HTMLImageElement) => (img.onclick = this.handleClick.bind(this))
|
||||
(e: HTMLImageElement) => (e.onclick = this.handleClick.bind(this))
|
||||
);
|
||||
}
|
||||
|
||||
private handleClick(event: Event) {
|
||||
(this.query(
|
||||
'#photo'
|
||||
) as HTMLImageElement).src = (event.target as HTMLImageElement).src;
|
||||
|
||||
const container = this.query('#container');
|
||||
Array.prototype.forEach.call(container.childNodes, (e: HTMLElement) =>
|
||||
e.remove()
|
||||
);
|
||||
container.appendChild((event.target as HTMLElement).cloneNode());
|
||||
PageImageViewer.show(this.element);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export const create = ({ header, timeline, footer }: Portfolio) => {
|
|||
|
||||
new ContainerPage(document.body, [
|
||||
new PageImageViewer(),
|
||||
new ContainerPage(document.body.querySelector('main'), [
|
||||
new ContainerPage(document.body.querySelector('.main'), [
|
||||
pageHeader,
|
||||
new PageTimeline(timeline),
|
||||
pageFooter,
|
||||
|
|
|
|||
7
src/page/theme-switcher/theme-switcher.html.ts
Normal file
7
src/page/theme-switcher/theme-switcher.html.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { html } from '../../model/misc';
|
||||
|
||||
import './theme-switcher.scss';
|
||||
|
||||
export const generate = (): html => `
|
||||
<input id="theme-switcher" type="checkbox" name="switch-theme"/>
|
||||
`;
|
||||
94
src/page/theme-switcher/theme-switcher.scss
Normal file
94
src/page/theme-switcher/theme-switcher.scss
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
@import '../../style/mixins';
|
||||
@import '../../style/vars';
|
||||
|
||||
@include responsive using($vars) {
|
||||
input[type='checkbox']#theme-switcher {
|
||||
@include on-large-screen {
|
||||
position: fixed;
|
||||
top: map_get($vars, $large-margin);
|
||||
right: map_get($vars, $large-margin);
|
||||
}
|
||||
|
||||
@include on-small-screen {
|
||||
position: relative;
|
||||
margin-top: map_get($vars, $small-margin);
|
||||
}
|
||||
|
||||
$size: map_get($vars, $icon-size);
|
||||
$small-size: 4 / 5 * $size;
|
||||
|
||||
z-index: 10;
|
||||
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
|
||||
width: 2 * $size;
|
||||
height: $size;
|
||||
|
||||
border-radius: 1000px;
|
||||
box-shadow: map_get($vars, $shadow1), map_get($vars, $shadow2);
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
display: block;
|
||||
border-radius: 1000px;
|
||||
|
||||
@include square($size);
|
||||
|
||||
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),
|
||||
background-color map_get($vars, $long-transition-time);
|
||||
}
|
||||
|
||||
&:before {
|
||||
left: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&: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;
|
||||
|
||||
@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-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:checked {
|
||||
&:before {
|
||||
background-color: map_get($vars, $normal-text-color);
|
||||
}
|
||||
&: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);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/page/theme-switcher/theme-switcher.ts
Normal file
40
src/page/theme-switcher/theme-switcher.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { PageElement } from '../../framework/page-element';
|
||||
import { createElement } from '../../framework/helper/create-element';
|
||||
import { generate } from './theme-switcher.html';
|
||||
import {
|
||||
isSystemLevelDarkModeEnabled,
|
||||
turnOnDarkMode,
|
||||
turnOnLightMode,
|
||||
} from '../../framework/helper/dark-mode';
|
||||
import { PageEvent, PageEventType } from '../../framework/page-event';
|
||||
import { EventBroadcaster } from '../../framework/event-broadcaster';
|
||||
|
||||
export class PageThemeSwitcher extends PageElement {
|
||||
public constructor() {
|
||||
super(createElement(generate()));
|
||||
if (isSystemLevelDarkModeEnabled()) {
|
||||
(this.element as HTMLInputElement).checked = true;
|
||||
}
|
||||
this.element.onchange = this.handleThemeChange.bind(this);
|
||||
}
|
||||
|
||||
protected handleEvent(event: PageEvent, parent: EventBroadcaster) {
|
||||
if (event.type === PageEventType.onLoad) {
|
||||
console.log('a');
|
||||
this.handleThemeChange();
|
||||
}
|
||||
}
|
||||
|
||||
private handleThemeChange() {
|
||||
const isDark = (this.element as HTMLInputElement).checked;
|
||||
if (isDark) {
|
||||
turnOnDarkMode();
|
||||
} else {
|
||||
turnOnLightMode();
|
||||
}
|
||||
this.eventBroadcaster.broadcastEvent({
|
||||
type: PageEventType.pageThemeChanged,
|
||||
data: isDark,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,112 +1,126 @@
|
|||
@import '../../../style/mixins';
|
||||
@import '../../../style/vars';
|
||||
|
||||
.timeline-element {
|
||||
display: flex;
|
||||
|
||||
.line {
|
||||
position: relative;
|
||||
border-left: $line-width solid $accent-color;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
@include square($icon-size);
|
||||
position: absolute;
|
||||
left: calc(-0.5 * #{$icon-size} - (1.5 * #{$line-width}));
|
||||
border: $line-width solid $accent-color;
|
||||
border-radius: 100%;
|
||||
background: $background;
|
||||
}
|
||||
|
||||
.date {
|
||||
@include insignificant-font();
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $breakpoint-width) {
|
||||
&:not(:first-of-type) .card {
|
||||
margin-top: $large-margin;
|
||||
}
|
||||
@include responsive() using ($vars) {
|
||||
.timeline-element {
|
||||
display: flex;
|
||||
width: map_get($vars, $body-width);
|
||||
margin: auto;
|
||||
|
||||
.line {
|
||||
&:before {
|
||||
top: calc(33% - #{$icon-size} / 2);
|
||||
}
|
||||
|
||||
.date {
|
||||
position: relative;
|
||||
top: calc(33% + #{$icon-size} / 2 + 1ch);
|
||||
transform: rotate(30deg);
|
||||
margin: 0 $normal-margin 0 calc(#{$line-width} + 1ex);
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-width) {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&:before {
|
||||
top: calc(50% - #{$icon-size} / 2);
|
||||
}
|
||||
|
||||
.line {
|
||||
@include center-children();
|
||||
justify-content: flex-start;
|
||||
height: 150px;
|
||||
width: 50%;
|
||||
|
||||
.date {
|
||||
margin-left: calc(#{$icon-size} / 2 + #{$small-margin});
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
@include card();
|
||||
overflow: hidden;
|
||||
|
||||
& > *:not(:first-child) {
|
||||
margin-top: $line-height;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@include sub-title-font();
|
||||
}
|
||||
|
||||
& > p {
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.more {
|
||||
overflow: hidden;
|
||||
height: 0;
|
||||
margin-top: 0;
|
||||
transition: height $long-transition-time;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
position: relative;
|
||||
margin-top: $small-margin;
|
||||
border-left: map_get($vars, $line-width) solid
|
||||
map_get($vars, $accent-color);
|
||||
|
||||
* {
|
||||
transition: opacity $long-transition-time;
|
||||
}
|
||||
|
||||
.show-more {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.show-less {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
&:before {
|
||||
content: '';
|
||||
@include square(map_get($vars, $icon-size));
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
left: calc(
|
||||
-0.5 * #{map_get($vars, $icon-size)} -
|
||||
(1.5 * #{map_get($vars, $line-width)})
|
||||
);
|
||||
border: map_get($vars, $line-width) solid map_get($vars, $accent-color);
|
||||
border-radius: 100%;
|
||||
background: map_get($vars, $background);
|
||||
}
|
||||
|
||||
.date {
|
||||
@include insignificant-font();
|
||||
color: map_get($vars, $accent-color);
|
||||
}
|
||||
}
|
||||
|
||||
@include on-large-screen {
|
||||
&:not(:first-of-type) .card {
|
||||
margin-top: map_get($vars, $large-margin);
|
||||
}
|
||||
|
||||
.line {
|
||||
&:before {
|
||||
top: calc(33% - #{map_get($vars, $icon-size)} / 2);
|
||||
}
|
||||
|
||||
.date {
|
||||
position: relative;
|
||||
top: calc(33% + #{map_get($vars, $icon-size)} / 2 + 1ch);
|
||||
transform: rotate(30deg);
|
||||
margin: 0 map_get($vars, $normal-margin) 0
|
||||
calc(#{map_get($vars, $line-width)} + 1ex);
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include on-small-screen {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&:before {
|
||||
top: calc(50% - #{map_get($vars, $icon-size)} / 2);
|
||||
}
|
||||
|
||||
.line {
|
||||
@include center-children();
|
||||
justify-content: flex-start;
|
||||
height: 150px;
|
||||
width: 50%;
|
||||
|
||||
.date {
|
||||
margin-left: calc(
|
||||
#{map_get($vars, $icon-size)} / 2 + #{map_get($vars, $small-margin)}
|
||||
);
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
@include card-base($vars);
|
||||
border-radius: map_get($vars, $border-radius);
|
||||
background-color: map_get($vars, $card-color);
|
||||
overflow: hidden;
|
||||
|
||||
& > *:not(:first-child) {
|
||||
margin-top: map_get($vars, $line-height);
|
||||
}
|
||||
|
||||
h2 {
|
||||
@include sub-title-font();
|
||||
}
|
||||
|
||||
& > p {
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.more {
|
||||
overflow: hidden;
|
||||
height: 0;
|
||||
margin-top: 0;
|
||||
transition: height map_get($vars, $long-transition-time);
|
||||
}
|
||||
|
||||
.buttons {
|
||||
position: relative;
|
||||
margin-top: map_get($vars, $small-margin);
|
||||
|
||||
* {
|
||||
transition: opacity map_get($vars, $long-transition-time);
|
||||
}
|
||||
|
||||
.show-more {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.show-less {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
@import '../../style/vars';
|
||||
@import '../../style/mixins';
|
||||
|
||||
#timeline {
|
||||
width: $body-width;
|
||||
margin: $large-margin auto 0 auto;
|
||||
|
||||
@media (max-width: $breakpoint-width) {
|
||||
margin: auto;
|
||||
@include responsive() using ($vars) {
|
||||
#timeline {
|
||||
@include on-large-screen {
|
||||
// workaround for IE
|
||||
& > :first-child {
|
||||
margin-top: map_get($vars, $large-margin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="512px" viewBox="0 0 512.00018 512" width="512px"><g><path d="m401.738281 442.710938c6.046875 9.242187 3.453125 21.636718-5.792969 27.683593-41.585937 27.195313-89.9375 41.582031-139.824218 41.605469-.039063 0-.082032 0-.121094 0-68.097656.003906-132.101562-26.234375-180.253906-73.890625-48.183594-47.683594-75.078125-111.46875-75.7343752-179.597656-.6601568-68.558594 25.4453122-133.210938 73.5039062-182.054688 48.066406-48.851562 112.265625-76 180.773437-76.45312475.5625 0 1.136719-.00390625 1.699219-.00390625 53.46875 0 104.816407 16.546875 148.613281 47.90625 43.277344 30.988281 75.519532 73.652344 93.238282 123.378906 3.707031 10.40625-1.722656 21.847656-12.125 25.554688-10.40625 3.707031-21.84375-1.722656-25.554688-12.125-30.898437-86.707032-112.84375-144.714844-204.195312-144.714844-.476563 0-.9375 0-1.414063.003906-57.796875.378906-111.964843 23.289063-152.523437 64.507813-40.546875 41.210937-62.570313 95.765625-62.015625 153.613281 1.132812 117.949219 98.019531 213.875 215.992187 213.875h.097656c42.097657-.019531 82.886719-12.148438 117.953126-35.082031 9.242187-6.046875 21.636718-3.453125 27.683593 5.792969zm110.261719-165.710938c0 50.179688-40.820312 91-91 91-32.0625 0-60.304688-16.667969-76.519531-41.796875-20.71875 26.058594-52.679688 42.796875-88.480469 42.796875-62.308594 0-113-50.691406-113-113s50.691406-113 113-113c28.277344 0 54.160156 10.441406 74 27.667969v-3.535157c0-11.042968 8.953125-20 20-20s20 8.957032 20 20v109.867188c0 28.121094 22.878906 51 51 51s51-22.878906 51-51c0-11.046875 8.953125-20 20-20s20 8.953125 20 20zm-183-21c0-40.25-32.746094-73-73-73-40.25 0-73 32.75-73 73 0 40.253906 32.75 73 73 73 40.253906 0 73-32.746094 73-73zm0 0" class="active-path" fill="#31343F"/></g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 512.002 512.002" style="enable-background:new 0 0 512.002 512.002;" xml:space="preserve" width="512px" height="512px" class="">
|
||||
<path d="M394.667,42.667h-128c-11.782,0-21.333,9.551-21.333,21.333v128c0,11.782,9.551,21.333,21.333,21.333h128 c11.782,0,21.333-9.551,21.333-21.333V64C416,52.218,406.449,42.667,394.667,42.667z M290.347,192 c7.707-22.268,32.007-34.072,54.275-26.365c12.366,4.28,22.085,13.999,26.365,26.365H290.347z M309.333,117.333 c0-11.782,9.551-21.333,21.333-21.333C342.449,96,352,105.551,352,117.333c0,11.782-9.551,21.333-21.333,21.333 C318.885,138.667,309.333,129.116,309.333,117.333z M394.667,192h-1.387c-4.028-18.843-16.331-34.868-33.493-43.627 c17.25-16.053,18.221-43.051,2.167-60.301c-16.053-17.25-43.051-18.221-60.301-2.167c-17.25,16.053-18.221,43.051-2.167,60.301 c0.696,0.748,1.419,1.471,2.167,2.167c-17.203,8.734-29.548,24.763-33.6,43.627h-1.387V64h128V192z" class="active-path" fill="#31343F"/>
|
||||
<path d="M432.429,0.002C432.357,0.001,432.285,0,432.214,0h-282.88c-2.835-0.016-5.56,1.097-7.573,3.093L56.427,88.427 c-1.997,2.013-3.11,4.738-3.093,7.573v389.547c0.058,14.586,11.868,26.395,26.453,26.453h352 c14.668,0.177,26.701-11.57,26.878-26.238c0.001-0.072,0.001-0.144,0.002-0.215V26.88 C458.844,12.213,447.097,0.179,432.429,0.002z M437.334,485.547c0,2.828-2.292,5.12-5.12,5.12h-352 c-2.818,0.236-5.293-1.858-5.529-4.675c-0.012-0.148-0.018-0.296-0.018-0.445V100.374l64-64v44.16 c-0.057,2.627-2.173,4.743-4.8,4.8h-27.2v21.333h32c12.352-2.351,21.299-13.133,21.333-25.707V21.333h272.213 c2.828,0,5.12,2.292,5.12,5.12V485.547z" class="active-path" fill="#31343F"/>
|
||||
<rect x="192" y="256" width="170.667" height="21.333" class="active-path" fill="#31343F"/>
|
||||
<rect x="149.333" y="309.333" width="213.333" height="21.333" class="active-path" fill="#31343F"/>
|
||||
<rect x="149.333" y="362.667" width="213.333" height="21.333" class="active-path" fill="#31343F"/>
|
||||
<rect x="149.333" y="416" width="170.667" height="21.333" class="active-path" fill="#31343F"/>
|
||||
<rect x="341.334" y="416" width="21.333" height="21.333" class="active-path" fill="#31343F"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
|
|
@ -1,48 +1,51 @@
|
|||
@import 'vars';
|
||||
@import 'mixins';
|
||||
|
||||
a {
|
||||
@include insignificant-font();
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
color: $accent-color;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
@include responsive() using ($vars) {
|
||||
a {
|
||||
@include insignificant-font();
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
color: map_get($vars, $accent-color);
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
|
||||
$border-shift: 10px;
|
||||
$border-shift: 10px;
|
||||
|
||||
transition: transform $long-transition-time;
|
||||
transition: transform map_get($vars, $long-transition-time);
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: $line-width;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
$card-color 0,
|
||||
transparentize($card-color, 1) 4px,
|
||||
transparentize($card-color, 1) calc(100% - 4px),
|
||||
$card-color 100%
|
||||
);
|
||||
}
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: map_get($vars, $line-width);
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
map_get($vars, $card-color) 0,
|
||||
transparentize(map_get($vars, $card-color), 1) 4px,
|
||||
transparentize(map_get($vars, $card-color), 1) calc(100% - 4px),
|
||||
map_get($vars, $card-color) 100%
|
||||
);
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: calc(100% + #{$border-shift});
|
||||
z-index: 0;
|
||||
border-bottom: $line-width dashed $accent-color;
|
||||
transition: transform $long-transition-time;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:after {
|
||||
transform: translateX(-$border-shift);
|
||||
content: '';
|
||||
display: block;
|
||||
width: calc(100% + #{$border-shift});
|
||||
z-index: 0;
|
||||
border-bottom: map_get($vars, $line-width) dashed
|
||||
map_get($vars, $accent-color);
|
||||
transition: transform map_get($vars, $long-transition-time);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:after {
|
||||
transform: translateX(-$border-shift);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +1,57 @@
|
|||
@import 'vars';
|
||||
|
||||
@mixin on-small-screen() {
|
||||
@media (max-width: $breakpoint-width) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin on-large-screen() {
|
||||
@media (min-width: $breakpoint-width) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin responsive() {
|
||||
html {
|
||||
@include on-small-screen {
|
||||
@content (map_merge($small-screen-variables, $light-theme-variables));
|
||||
&[theme='dark'] {
|
||||
@content (map_merge($small-screen-variables, $dark-theme-variables));
|
||||
}
|
||||
}
|
||||
}
|
||||
@include on-large-screen {
|
||||
html {
|
||||
@content (map_merge($large-screen-variables, $light-theme-variables));
|
||||
&[theme='dark'] {
|
||||
@content (map_merge($large-screen-variables, $dark-theme-variables));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin center-children() {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
%card {
|
||||
@mixin card-base($vars) {
|
||||
text-align: center;
|
||||
padding: $normal-margin;
|
||||
box-shadow: $shadow1, $shadow2;
|
||||
padding: map_get($vars, $normal-margin);
|
||||
box-shadow: map_get($vars, $shadow1), map_get($vars, $shadow2);
|
||||
z-index: 1;
|
||||
|
||||
@media (min-width: $breakpoint-width) {
|
||||
transition: box-shadow $long-transition-time;
|
||||
@include on-large-screen {
|
||||
transition: box-shadow map_get($vars, $long-transition-time);
|
||||
|
||||
&:hover {
|
||||
box-shadow: $shadow3, $shadow2;
|
||||
box-shadow: map_get($vars, $shadow3), map_get($vars, $shadow2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin card() {
|
||||
@extend %card;
|
||||
border-radius: $border-radius;
|
||||
background-color: $card-color;
|
||||
}
|
||||
|
||||
@mixin important-card() {
|
||||
@extend %card;
|
||||
background-color: $accent-color;
|
||||
|
||||
* {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin square($size) {
|
||||
width: $size;
|
||||
height: $size;
|
||||
|
|
@ -51,7 +67,7 @@
|
|||
font-style: normal;
|
||||
line-height: 1;
|
||||
|
||||
@media (max-width: $breakpoint-width) {
|
||||
@include on-small-screen {
|
||||
font-size: 3rem;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +1,114 @@
|
|||
@import 'fonts';
|
||||
|
||||
$background: #ffffff;
|
||||
$_id_value: 0;
|
||||
@function id() {
|
||||
$_id_value: $_id_value + 1 !global;
|
||||
@return $_id_value;
|
||||
}
|
||||
|
||||
$normal-text-color: #31343f;
|
||||
$light-text-color: #7a7d8e;
|
||||
$inverse-text-color: #ffffff;
|
||||
/* NAMES */
|
||||
$background: id();
|
||||
|
||||
$card-color: #ffffff;
|
||||
$accent-color: #aa4465;
|
||||
$scrollbar-color: #ffd6d6;
|
||||
$normal-text-color: id();
|
||||
$light-text-color: id();
|
||||
$important-card-text-color: id();
|
||||
|
||||
$inverse-text-color: id();
|
||||
$card-color: id();
|
||||
$important-card-color: id();
|
||||
$theme-switcher-color: id();
|
||||
|
||||
$accent-color: id();
|
||||
$scrollbar-color: id();
|
||||
|
||||
$short-transition-time: id();
|
||||
$long-transition-time: id();
|
||||
$line-width: id();
|
||||
$border-radius: id();
|
||||
$breakpoint-width: id();
|
||||
|
||||
$large-margin: id();
|
||||
$normal-margin: id();
|
||||
$small-margin: id();
|
||||
$line-height: id();
|
||||
|
||||
$shadow1: id();
|
||||
$shadow2: id();
|
||||
$shadow3: id();
|
||||
|
||||
$body-width: id();
|
||||
|
||||
$icon-size: id();
|
||||
/**/
|
||||
|
||||
$short-transition-time: 220ms;
|
||||
$long-transition-time: 350ms;
|
||||
$line-width: 3px;
|
||||
$border-radius: var(--border-radius);
|
||||
$breakpoint-width: 925px;
|
||||
|
||||
$large-margin: var(--large-margin);
|
||||
$normal-margin: var(--normal-margin);
|
||||
$small-margin: var(--small-margin);
|
||||
$line-height: 18px;
|
||||
$universal-variables: (
|
||||
$short-transition-time: 220ms,
|
||||
$long-transition-time: 350ms,
|
||||
$line-width: 3px,
|
||||
$line-height: 18px,
|
||||
$shadow3: 0 0 15px 4px rgba(0, 0, 0, 0.1),
|
||||
);
|
||||
|
||||
$shadow1: var(--shadow1);
|
||||
$shadow2: var(--shadow2);
|
||||
$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
|
||||
);
|
||||
|
||||
$icon-size: var(--icon-size);
|
||||
$body-width: var(--body-width);
|
||||
$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
|
||||
);
|
||||
|
||||
:root {
|
||||
--large-margin: 70px;
|
||||
--normal-margin: 45px;
|
||||
--small-margin: 25px;
|
||||
--icon-size: 35px;
|
||||
--body-width: 765px;
|
||||
--shadow1: 0 0 10px 2px rgba(0, 0, 0, 0.075);
|
||||
--shadow2: 0 0 1px rgba(0, 0, 0, 0.2);
|
||||
--border-radius: 15px;
|
||||
$large-screen-variables: map_merge(
|
||||
(
|
||||
$border-radius: 15px,
|
||||
$large-margin: 70px,
|
||||
$normal-margin: 45px,
|
||||
$small-margin: 25px,
|
||||
$shadow1: 0 0 10px 2px rgba(0, 0, 0, 0.075),
|
||||
$shadow2: 0 0 1px rgba(0, 0, 0, 0.2),
|
||||
$icon-size: 35px,
|
||||
$body-width: 765px,
|
||||
),
|
||||
$universal-variables
|
||||
);
|
||||
|
||||
@media (max-width: $breakpoint-width) {
|
||||
--large-margin: 60px;
|
||||
--normal-margin: 30px;
|
||||
--small-margin: 15px;
|
||||
--icon-size: 25px;
|
||||
--body-width: 90%;
|
||||
--shadow1: 0 0 10px 2px rgba(0, 0, 0, 0.05);
|
||||
--shadow2: 0 0 1px rgba(0, 0, 0, 0.125);
|
||||
--border-radius: 10px;
|
||||
}
|
||||
}
|
||||
$small-screen-variables: map_merge(
|
||||
(
|
||||
$border-radius: 10px,
|
||||
$large-margin: 60px,
|
||||
$normal-margin: 30px,
|
||||
$small-margin: 15px,
|
||||
$shadow1: 0 0 10px 2px rgba(0, 0, 0, 0.05),
|
||||
$shadow2: 0 0 1px rgba(0, 0, 0, 0.125),
|
||||
$icon-size: 25px,
|
||||
$body-width: 90%,
|
||||
),
|
||||
$universal-variables
|
||||
);
|
||||
|
|
|
|||
111
src/styles.scss
111
src/styles.scss
|
|
@ -3,68 +3,87 @@
|
|||
@import 'style/a';
|
||||
@import 'framework/primitives/primitives';
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
color: $normal-text-color;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
::-moz-selection {
|
||||
background: $accent-color;
|
||||
color: $inverse-text-color;
|
||||
}
|
||||
::selection {
|
||||
background: $accent-color;
|
||||
color: $inverse-text-color;
|
||||
}
|
||||
|
||||
html {
|
||||
background-color: $background;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
@include main-font();
|
||||
height: 100%;
|
||||
//noinspection CssInvalidFunction
|
||||
padding: env(safe-area-inset-top, 20px) env(safe-area-inset-right, 20px)
|
||||
env(safe-area-inset-bottom, 20px) env(safe-area-inset-left, 20px);
|
||||
|
||||
& > main {
|
||||
@include responsive() using ($vars) {
|
||||
& {
|
||||
background-color: map_get($vars, $background);
|
||||
height: 100%;
|
||||
|
||||
@include on-small-screen {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: hotpink;
|
||||
}
|
||||
|
||||
body {
|
||||
@include main-font();
|
||||
|
||||
//noinspection CssInvalidFunction
|
||||
padding: env(safe-area-inset-top, 20px) env(safe-area-inset-right, 20px)
|
||||
env(safe-area-inset-bottom, 20px) env(safe-area-inset-left, 20px);
|
||||
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
color: map_get($vars, $normal-text-color);
|
||||
hyphens: auto;
|
||||
|
||||
:focus {
|
||||
/* border: 2px solid map_get($vars, $accent-color);
|
||||
outline: 0;
|
||||
border-radius: map_get($vars, $border-radius);*/
|
||||
}
|
||||
}
|
||||
|
||||
img,
|
||||
video {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
::-moz-selection {
|
||||
background: map_get($vars, $accent-color);
|
||||
color: map_get($vars, $inverse-text-color);
|
||||
}
|
||||
::selection {
|
||||
background: map_get($vars, $accent-color);
|
||||
color: map_get($vars, $inverse-text-color);
|
||||
}
|
||||
|
||||
.main {
|
||||
height: 100vh;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
|
||||
will-change: transform;
|
||||
|
||||
-webkit-overflow-scrolling: touch;
|
||||
perspective: 5px;
|
||||
perspective-origin: center center;
|
||||
|
||||
noscript {
|
||||
@include square(100%);
|
||||
@include center-children();
|
||||
}
|
||||
|
||||
@media (min-width: $breakpoint-width) {
|
||||
@include on-large-screen {
|
||||
&::-webkit-scrollbar-track,
|
||||
&::-webkit-scrollbar {
|
||||
background-color: transparent;
|
||||
width: 12px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: $accent-color;
|
||||
border-radius: $border-radius;
|
||||
background-color: map_get($vars, $accent-color);
|
||||
border-radius: map_get($vars, $border-radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
img,
|
||||
video {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-width) {
|
||||
html {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
|||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
|
||||
const Sharp = require('responsive-loader/sharp');
|
||||
const Sass = require('sass');
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
|
|
@ -15,7 +16,7 @@ module.exports = {
|
|||
},
|
||||
devServer: {
|
||||
host: '0.0.0.0',
|
||||
// disableHostCheck: true,
|
||||
//disableHostCheck: true,
|
||||
},
|
||||
optimization: {
|
||||
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
|
||||
|
|
@ -134,6 +135,7 @@ module.exports = {
|
|||
loader: 'sass-loader',
|
||||
options: {
|
||||
sourceMap: true,
|
||||
implementation: Sass,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue