Controls turn into tapbar
11
package-lock.json
generated
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@solid-primitives/i18n": "^1.1.0",
|
||||
"@solid-primitives/intersection-observer": "^1.3.0",
|
||||
"normalize.css": "^8.0.1",
|
||||
"solid-js": "^1.3.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -1230,6 +1231,11 @@
|
||||
"integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/normalize.css": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
|
||||
"integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
@@ -2237,6 +2243,11 @@
|
||||
"integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize.css": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
|
||||
"integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"dependencies": {
|
||||
"@solid-primitives/i18n": "^1.1.0",
|
||||
"@solid-primitives/intersection-observer": "^1.3.0",
|
||||
"normalize.css": "^8.0.1",
|
||||
"solid-js": "^1.3.13"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
src/.DS_Store
vendored
BIN
src/assets/.DS_Store
vendored
3
src/assets/icons/globe.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-globe" viewBox="0 0 16 16">
|
||||
<path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm7.5-6.923c-.67.204-1.335.82-1.887 1.855A7.97 7.97 0 0 0 5.145 4H7.5V1.077zM4.09 4a9.267 9.267 0 0 1 .64-1.539 6.7 6.7 0 0 1 .597-.933A7.025 7.025 0 0 0 2.255 4H4.09zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a6.958 6.958 0 0 0-.656 2.5h2.49zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5H4.847zM8.5 5v2.5h2.99a12.495 12.495 0 0 0-.337-2.5H8.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5H4.51zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5H8.5zM5.145 12c.138.386.295.744.468 1.068.552 1.035 1.218 1.65 1.887 1.855V12H5.145zm.182 2.472a6.696 6.696 0 0 1-.597-.933A9.268 9.268 0 0 1 4.09 12H2.255a7.024 7.024 0 0 0 3.072 2.472zM3.82 11a13.652 13.652 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5H3.82zm6.853 3.472A7.024 7.024 0 0 0 13.745 12H11.91a9.27 9.27 0 0 1-.64 1.539 6.688 6.688 0 0 1-.597.933zM8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855.173-.324.33-.682.468-1.068H8.5zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.65 13.65 0 0 1-.312 2.5zm2.802-3.5a6.959 6.959 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5h2.49zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7.024 7.024 0 0 0-3.072-2.472c.218.284.418.598.597.933zM10.855 4a7.966 7.966 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4h2.355z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
3
src/assets/icons/mail.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-envelope" viewBox="0 0 16 16">
|
||||
<path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4Zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1H2Zm13 2.383-4.708 2.825L15 11.105V5.383Zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741ZM1 11.105l4.708-2.897L1 5.383v5.722Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
BIN
src/assets/projects/flexpatrolPreview.png
Normal file
|
After Width: | Height: | Size: 665 KiB |
BIN
src/assets/projects/gamesPreview.png
Normal file
|
After Width: | Height: | Size: 488 KiB |
BIN
src/assets/projects/studybuddyPreview.png
Normal file
|
After Width: | Height: | Size: 211 KiB |
BIN
src/assets/projects/warframePreview.png
Normal file
|
After Width: | Height: | Size: 316 KiB |
BIN
src/assets/projects/worktimePreview.png
Normal file
|
After Width: | Height: | Size: 180 KiB |
@@ -5,13 +5,14 @@
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
padding: var(--gap-sm);
|
||||
border-radius: 12px;
|
||||
border-radius: var(--radius-md);
|
||||
width: 64px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.chapters {
|
||||
position: relative;
|
||||
height: calc(64px * 3);
|
||||
height: calc(70px * 3);
|
||||
width: 100%;
|
||||
margin-bottom: var(--gap-md);
|
||||
}
|
||||
@@ -53,18 +54,16 @@
|
||||
background: var(--clr-bg);
|
||||
border-radius: 20px;
|
||||
aspect-ratio: 1/1;
|
||||
transition: width .6s, height .6s;
|
||||
transition: width .6s, height .6s, transform .6s;
|
||||
width: 0;
|
||||
height: 0;
|
||||
transform: translate(-50%, 50%);
|
||||
}
|
||||
|
||||
.selected {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.control:nth-child(1), .blob:nth-child(1) {
|
||||
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.control:nth-child(2), .blob:nth-child(2) {
|
||||
@@ -74,3 +73,42 @@
|
||||
.control:nth-child(3), .blob:nth-child(3) {
|
||||
top: calc(70px * 2);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1080px) {
|
||||
.Controls {
|
||||
top: unset;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: calc(100% - var(--gap-md));
|
||||
height: 64px;
|
||||
border-radius: 0;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.chapters {
|
||||
width: calc(70px * 3);
|
||||
height: 64px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.control, .blob {
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.control, .control > img {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.control:nth-child(2), .blob:nth-child(2) {
|
||||
top: unset;
|
||||
left: 70px;
|
||||
}
|
||||
|
||||
.control:nth-child(3), .blob:nth-child(3) {
|
||||
top: unset;
|
||||
left: calc(70px * 2);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import styles from './Controls.module.css';
|
||||
import LanguageSelector from './LanguageSelector';
|
||||
import homeIcon from '../assets/icons/home.svg'
|
||||
import gridIcon from '../assets/icons/grid.svg'
|
||||
import mailIcon from '../assets/icons/mail.svg'
|
||||
|
||||
export default () => {
|
||||
const [store, {setVisibleChapter}] = useStore() as Store;
|
||||
@@ -14,7 +15,7 @@ export default () => {
|
||||
const chapters = [
|
||||
{ name: 'home', icon: homeIcon },
|
||||
{ name: 'projects', icon: gridIcon },
|
||||
{ name: 'whatever', icon: homeIcon },
|
||||
{ name: 'whatever', icon: mailIcon },
|
||||
]
|
||||
|
||||
const selectChapter = (chapterName: string) => {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
}
|
||||
|
||||
.gradientText {
|
||||
font-size: 72px;
|
||||
font-size: 86px;
|
||||
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
.LanguageSelector {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: max-content;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.LanguageSelector > img {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1080px) {
|
||||
.LanguageSelector {
|
||||
position: absolute;
|
||||
right: var(--gap-lg);
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
import { useI18n } from "@solid-primitives/i18n";
|
||||
|
||||
import styles from './LanguageSelector.module.css';
|
||||
import globeIcon from '../assets/icons/globe.svg';
|
||||
|
||||
export default () => {
|
||||
const [t, { locale }] = useI18n();
|
||||
|
||||
const avaliableLocales = ['en', 'ru'];
|
||||
const preferredLocales = navigator.languages.map((lang) => lang.substring(0, 2));
|
||||
for (const lang of preferredLocales) {
|
||||
if (avaliableLocales.includes(lang)) {
|
||||
locale(lang);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
@@ -19,6 +29,7 @@ export default () => {
|
||||
}}
|
||||
class={styles.LanguageSelector}
|
||||
>
|
||||
<img src={globeIcon}/>
|
||||
{t('lang')}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
.Project {
|
||||
position: relative;
|
||||
background: var(--clr-bg-trietary);
|
||||
border-radius: var(--radius-md);
|
||||
width: calc(33% - var(--gap-lg));
|
||||
min-width: 320px;
|
||||
}
|
||||
|
||||
.preview {
|
||||
position: relative;
|
||||
display: block;
|
||||
border-radius: var(--radius-md) var(--radius-md) 0 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 250px;
|
||||
object-fit: cover;
|
||||
object-position: top;
|
||||
}
|
||||
|
||||
.body {
|
||||
padding: var(--gap-sm);
|
||||
text-align: left;
|
||||
height: 107px;
|
||||
}
|
||||
|
||||
.description {
|
||||
padding-top: var(--gap-sm);
|
||||
font: var(--font-sm);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
import styles from './Project.module.css';
|
||||
import { useI18n } from "@solid-primitives/i18n";
|
||||
|
||||
import styles from './Project.module.css';
|
||||
import type { Project } from './projectList';
|
||||
|
||||
export default (props: {project: Project}) => {
|
||||
const [t] = useI18n();
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<div class={styles.Project}>
|
||||
|
||||
<img
|
||||
class={styles.preview}
|
||||
src={props.project.preview}
|
||||
/>
|
||||
<div
|
||||
class={styles.body}
|
||||
>
|
||||
<a
|
||||
href={props.project.link}
|
||||
target="_blank"
|
||||
>
|
||||
<h2>{props.project.name}</h2>
|
||||
</a>
|
||||
<div class={styles.description}>
|
||||
{t(props.project.descriptionSlug)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -4,4 +4,7 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--gap-lg);
|
||||
margin-left: 130px;
|
||||
margin-right: 130px;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { For } from 'solid-js';
|
||||
import { createViewportObserver } from '@solid-primitives/intersection-observer';
|
||||
|
||||
import { useStore } from '../../store/index';
|
||||
import type { Store } from '../../store/index';
|
||||
import { scrollHereWhenSelected } from "../../utlis/scroll";
|
||||
import styles from './Projects.module.css';
|
||||
import projects from './projectList';
|
||||
import Project from './Project';
|
||||
import type { Project as ProjectType } from './projectList';
|
||||
|
||||
export default () => {
|
||||
const [store, { setVisibleChapter }] = useStore() as Store;
|
||||
const [observer] = createViewportObserver({threshold: 0.9});
|
||||
const [observer] = createViewportObserver({threshold: 0.5});
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -19,6 +23,11 @@ export default () => {
|
||||
ref={(element) => scrollHereWhenSelected(element, store, 'projects')}
|
||||
class={styles.Projects}
|
||||
>
|
||||
<For each={projects}>{(project: ProjectType) =>
|
||||
<Project
|
||||
project={project}
|
||||
/>
|
||||
}</For>
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
52
src/components/Projects/projectList.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import flexpatrolPreview from '../../assets/projects/flexpatrolPreview.png';
|
||||
import gamesPreview from '../../assets/projects/gamesPreview.png';
|
||||
import warframePreview from '../../assets/projects/warframePreview.png';
|
||||
import worktimePreview from '../../assets/projects/worktimePreview.png';
|
||||
import studybuddyPreview from '../../assets/projects/studybuddyPreview.png';
|
||||
|
||||
export class Project {
|
||||
name: string;
|
||||
preview: string;
|
||||
link: string;
|
||||
descriptionSlug: string;
|
||||
|
||||
constructor(project: Project) {
|
||||
this.name = project.name
|
||||
this.preview = project.preview
|
||||
this.link = project.link
|
||||
this.descriptionSlug = project.descriptionSlug
|
||||
}
|
||||
}
|
||||
|
||||
export default [
|
||||
new Project({
|
||||
name: 'flexpatrol.ru',
|
||||
preview: flexpatrolPreview,
|
||||
link: 'https://flexpatrol.ru',
|
||||
descriptionSlug: 'flexpatrol_desc'
|
||||
}),
|
||||
new Project({
|
||||
name: 'My Games',
|
||||
preview: gamesPreview,
|
||||
link: 'https://games.anatolykopyl.ru',
|
||||
descriptionSlug: 'games_desc'
|
||||
}),
|
||||
new Project({
|
||||
name: 'Warframe Center',
|
||||
preview: warframePreview,
|
||||
link: 'https://warframe.center',
|
||||
descriptionSlug: 'warframe_desc'
|
||||
}),
|
||||
new Project({
|
||||
name: 'Worktime',
|
||||
preview: worktimePreview,
|
||||
link: 'https://anatolykopyl.github.io/worktime',
|
||||
descriptionSlug: 'worktime_desc'
|
||||
}),
|
||||
new Project({
|
||||
name: 'Studybuddy',
|
||||
preview: studybuddyPreview,
|
||||
link: 'https://studybuddy.top',
|
||||
descriptionSlug: 'studybuddy_desc'
|
||||
}),
|
||||
]
|
||||
@@ -1,24 +1,42 @@
|
||||
@import 'normalize.css';
|
||||
|
||||
:root {
|
||||
--clr-bg: rgb(7, 6, 9);
|
||||
--clr-bg-secondary: hsl(260, 20%, 16%);
|
||||
--clr-bg-trietary: hsl(260, 20%, 10%);
|
||||
--clr-text: white;
|
||||
|
||||
--gap-sm: 18px;
|
||||
--gap-md: 36px;
|
||||
--gap-lg: 72px;
|
||||
|
||||
--radius-md: 12px;
|
||||
|
||||
--ff-default: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
--font-sm: normal normal 300 16px var(--ff-default);
|
||||
--font-md: normal normal 400 18px var(--ff-default);
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
color: var(--clr-text);
|
||||
font: var(--font-md);
|
||||
text-align: center;
|
||||
background: var(--clr-bg);
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"lang": "en",
|
||||
"my_name": "Anatoly Kopyl",
|
||||
"tagline": "Professional fullstack developer with standards"
|
||||
"tagline": "Professional fullstack developer with standards",
|
||||
"flexpatrol_desc": "A landing page for a gaming squad with info on their servers and their status.",
|
||||
"games_desc": "A page with thumbnails and links to my games.",
|
||||
"warframe_desc": "A service that monitors prices of items on warframe.market and calculates profitable gaps between them.",
|
||||
"worktime_desc": "A PWA that I use daily to track my time spent working. Full offline support.",
|
||||
"studybuddy_desc": "A PWA for splitting into groups or taking topics for an assignment."
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"lang": "ru",
|
||||
"my_name": "Анатолий Копыл",
|
||||
"tagline": "Профессиональный fullstack разработчик со стандартами"
|
||||
"tagline": "Профессиональный fullstack разработчик со стандартами",
|
||||
"flexpatrol_desc": "Лендинг для сквада геймеров, с информацией об их серверах и их статусе."
|
||||
}
|
||||
|
||||