diff --git a/.idea/.idea.keroosha-landing.dir/.idea/.gitignore b/.idea/.idea.keroosha-landing.dir/.idea/.gitignore new file mode 100644 index 0000000..f9cb1f8 --- /dev/null +++ b/.idea/.idea.keroosha-landing.dir/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/modules.xml +/projectSettingsUpdater.xml +/.idea.keroosha-landing.iml +/contentModel.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.keroosha-landing.dir/.idea/codeStyles/Project.xml b/.idea/.idea.keroosha-landing.dir/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..f45e41f --- /dev/null +++ b/.idea/.idea.keroosha-landing.dir/.idea/codeStyles/Project.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.keroosha-landing.dir/.idea/codeStyles/codeStyleConfig.xml b/.idea/.idea.keroosha-landing.dir/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/.idea.keroosha-landing.dir/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.keroosha-landing.dir/.idea/encodings.xml b/.idea/.idea.keroosha-landing.dir/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.keroosha-landing.dir/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.keroosha-landing.dir/.idea/indexLayout.xml b/.idea/.idea.keroosha-landing.dir/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.keroosha-landing.dir/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.keroosha-landing.dir/.idea/jsLibraryMappings.xml b/.idea/.idea.keroosha-landing.dir/.idea/jsLibraryMappings.xml new file mode 100644 index 0000000..d23208f --- /dev/null +++ b/.idea/.idea.keroosha-landing.dir/.idea/jsLibraryMappings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/.idea.keroosha-landing.dir/.idea/prettier.xml b/.idea/.idea.keroosha-landing.dir/.idea/prettier.xml new file mode 100644 index 0000000..0c83ac4 --- /dev/null +++ b/.idea/.idea.keroosha-landing.dir/.idea/prettier.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.keroosha-landing.dir/.idea/vcs.xml b/.idea/.idea.keroosha-landing.dir/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/.idea.keroosha-landing.dir/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..94d737c --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,3 @@ +{ + "printWidth": 120 +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 64b1b9c..8061e4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "keroosha-landing", "version": "0.1.0", "dependencies": { + "@fortawesome/fontawesome-free": "6.4.0", "@types/node": "20.3.1", "@types/react": "18.2.12", "@types/react-dom": "18.2.5", @@ -18,6 +19,9 @@ "react-dom": "18.2.0", "tailwindcss": "3.3.2", "typescript": "5.1.3" + }, + "devDependencies": { + "prettier": "2.8.8" } }, "node_modules/@alloc/quick-lru": { @@ -31,6 +35,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@fortawesome/fontawesome-free": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz", + "integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -1118,6 +1131,21 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", diff --git a/package.json b/package.json index 4ab3ce0..6f6db03 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,11 @@ "start": "next start", "lint": "next lint" }, + "devDependencies": { + "prettier": "2.8.8" + }, "dependencies": { + "@fortawesome/fontawesome-free": "6.4.0", "@types/node": "20.3.1", "@types/react": "18.2.12", "@types/react-dom": "18.2.5", diff --git a/src/app/globals.css b/src/app/globals.css index fd81e88..5d9a31f 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,27 +1,305 @@ +@import "@fortawesome/fontawesome-free/css/fontawesome.css"; +@import "@fortawesome/fontawesome-free/css/brands.css"; +@import "@fortawesome/fontawesome-free/css/solid.css"; @tailwind base; @tailwind components; @tailwind utilities; -:root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; +/* https://github.com/cat-street/deus-ex-ebook/blob/master/deusexpad.css */ +.page { + font-family: 'Open Sans', sans-serif; + color: #fff; + padding: 2vh 7vw; + position: relative; + background-color: black; } -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - } +.ebook { + position: relative; + background-color: #161515; + border-radius: 20px; + border-bottom: 5px ridge #b2c2c2; /* accidental nice effect */ + min-height: 600px; + min-width: 300px; + max-width: 1400px; + overflow: hidden; + margin: auto; } -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); +/* Decorations and top icons */ + +.ebook__side-gradient { + position: absolute; } + +.ebook__side-gradient_top, +.ebook__side-gradient_bottom { + height: 100px; + width: 100%; +} + +.ebook__side-gradient_top { + background-image: linear-gradient(#133333, #161515); + border-top-left-radius: 20px; + border-top-right-radius: 20px; +} + +.ebook__side-gradient_bottom { + bottom: 0; + background-image: linear-gradient(#161515, #133333); + border-bottom-left-radius: 20px; + border-bottom-right-radius: 20px; +} + +.ebook__side-gradient_left, +.ebook__side-gradient_right { + height: 100%; + width: 100px; +} + +.ebook__side-gradient_left { + background-image: linear-gradient(to right, #133333, #161515); +} + +.ebook__side-gradient_right { + right: 0; + background-image: linear-gradient(to left, #133333, #161515); +} + +.ebook__corner-gradient { + height: 100px; + width: 100px; + position: absolute; +} + +.ebook__corner-gradient_top-left { + top: 0; + left: 0; + background-image: radial-gradient(farthest-side at 100% 100%, #161515, #133333); + border-top-left-radius: 20px; +} + +.ebook__corner-gradient_top-right { + top: 0; + right: 0; + background-image: radial-gradient(farthest-side at 0% 100%, #161515, #133333); + border-top-right-radius: 20px; +} + +.ebook__corner-gradient_bottom-left { + bottom: 0; + left: 0; + background-image: radial-gradient(farthest-side at 100% 0%, #161515, #133333); + border-bottom-left-radius: 20px; +} + +.ebook__corner-gradient_bottom-right { + bottom: 0; + right: 0; + background-image: radial-gradient(farthest-side at 0% 0%, #161515, #133333); + border-bottom-right-radius: 20px; +} + +.ebook__dots-background { + position: absolute; + width: calc(100% - 30px); + height: 100%; + background-image: radial-gradient(#153939 3%, transparent 3%); + background-size: 100px 100px; +} + +.ebook__vertical-borders { + position: absolute; + border-left: 1px solid #a6c9c9; + border-right: 1px solid #a6c9c9; + right: 25px; + left: 25px; + top: 25px; + bottom: 25px; +} + +.ebook__icons { + position: absolute; + width: 100%; + display: flex; + justify-content: space-between; + box-sizing: border-box; + padding: 2rem 3rem 0; + font-size: 1.2rem !important; +} + +.ebook__icon_book { + font-size: 2rem !important; +} + +.ebook__flare { + opacity: .05; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: -55%; + background-color: #fff; + transform: skewX(-10deg); +} + +/* Content */ + +.ebook__content { + position: relative; + margin: 100px; + font-size: 1.2rem; + text-shadow: 0 0 1rem #14c1c1, 0 0 1rem #189494, 0 0 1rem #134848; +} + +.article { + transition: opacity .3s; +} + +.article__title { + font-family: 'Ubuntu', sans-serif; + text-transform: uppercase; + font-size: 1.2rem; + margin: 0 0 2.4rem; + text-align: center; +} + +.article__subtitle { + font-style: italic; + margin: 0 0 2rem; +} + +.article__subtitle_no-margin-bottom { + margin-bottom: 0; +} + +.article__image { + width: 25%; + float: right; + border-radius: 1rem; + margin: 0 0 1rem 2rem; + opacity: .5; + transition: opacity .2s, box-shadow .2s; + cursor: pointer; +} + +.article__image:hover { + opacity: 1; + box-shadow: 0 0 .5rem #14c1c1, 0 0 .5rem #189494, 0 0 .5rem #134848; +} + +.article__text { + text-align: justify; + margin: 0 0 1rem; + line-height: 2rem; +} + +.navigation { + text-align: center; + font-size: 1.5rem; +} + +.navigation__list { + list-style: none; + padding: 0; +} + +.navigation__item { + display: inline; + padding: 0 .1rem; +} + +.navigation__link { + text-decoration: none; + color: #fff; +} + +.lightbox { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + z-index: 1; + background-color: rgba(0, 0, 0, .75); + display: flex; + justify-content: center; + align-items: center; + padding: 40px; + cursor: pointer; +} + +.lightbox__item { + max-width: 95%; + max-height: 95%; +} + +.lightbox_hidden { + visibility: hidden; +} + +@media screen and (max-width: 992px) { + .page { + padding: 2vh 1vw; + } + + .ebook__content { + font-size: 1rem; + margin: 80px; + } + + .article__title { + font-size: 1rem; + margin-bottom: 2rem; + } + + .article__subtitle { + margin-bottom: 1.6rem; + } + + .article__text { + margin-bottom: 1.6rem; + line-height: 1.6rem; + } + + .ebook__icons { + font-size: .7rem !important; + } + + .ebook__icon_book { + font-size: 1.75rem !important; + } + + .article__image { + width: 50%; + opacity: .8; + box-shadow: 0 0 .5rem #14c1c1, 0 0 .5rem #189494, 0 0 .5rem #134848; + } +} + +@media screen and (max-width: 576px) { + .ebook__content { + font-size: .8rem; + margin: 60px; + } + + .article__title { + font-size: .8rem; + margin-bottom: 1.6rem; + } + + .article__subtitle { + margin-bottom: 1rem; + } + + .article__text { + margin-bottom: 1rem; + line-height: 1rem; + } + + .article__image { + width: 100%; + opacity: .8; + box-shadow: 0 0 .5rem #14c1c1, 0 0 .5rem #189494, 0 0 .5rem #134848; + } +} \ No newline at end of file diff --git a/src/app/page.tsx b/src/app/page.tsx index 7e80296..1f7cd14 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,113 +1,65 @@ -import Image from 'next/image' +import { DxBook, DxBookPage, DxBookPageIconsProps, DxIcon, DxParagraph } from "@/components"; +import React, { FC, PropsWithChildren } from "react"; +import { EmailLink, HhLink, IconLink, LinkTargetBlank, SpbDotNetLink } from "@/components/links"; + +const FlexList: FC = ({ children }) =>
{children}
; + +const left: DxBookPageIconsProps["left"] = [ + , + , + , + , +]; + +const right: DxBookPageIconsProps["right"] = []; + +const SkillzLine: FC = ({ className, children }) => ( +
+ + {children} +
+); + +const Block: FC = ({ children, title }) => ( +
+ {title} + {children} +
+); export default function Home() { return ( -
-
-

- Get started by editing  - src/app/page.tsx -

- -
+ + + + .NET (C#/FSharp) + PostgreSQL + GraphQL + OAuth2 (Idserver4/KeyCloak) + GNU/Linux + Node.JS + Docker + React + -
- Next.js Logo -
+ + + - -
- ) + + + + + + + ); } diff --git a/src/components/index.tsx b/src/components/index.tsx new file mode 100644 index 0000000..e33dbbb --- /dev/null +++ b/src/components/index.tsx @@ -0,0 +1,101 @@ +import React, { FC, PropsWithChildren } from "react"; + +type RNode = PropsWithChildren["children"]; +type DivProps = React.DetailedHTMLProps, HTMLDivElement>; +type IProps = React.DetailedHTMLProps, HTMLElement>; +type ImgProps = React.DetailedHTMLProps, HTMLImageElement>; +type PProps = React.DetailedHTMLProps, HTMLParagraphElement>; + +export type DxBookProps = DivProps & { iconsProps?: DxBookPageIconsProps }; +export type DxBookPageProps = PropsWithChildren & { title: string; subTitle: string }; +export type DxBookPageIconsProps = { left?: RNode; right?: RNode }; + +const DxBookPageDecoration = () => ( + <> +
+
+
+
+
+
+
+
+
+
+ +); + +const DxBookPageIcons: FC = ({ left, right }) => ( + <> +
+
{left}
+
{right}
+
+ +); + +const DxNavigationStub: FC = () => ( + +); + +export const DxBookPage: FC = ({ children, subTitle, title }) => ( +
+
+
+

{title}

+

{subTitle}

+
+ {children} +
+
+); + +export const DxParagraph: FC = ({ children, ...rest }) => ( +

+ {children} +

+); + +export const DxIcon: FC = ({ children, ...rest }) => ( + +); + +export const DxImage: FC = ({ ...props }) => ( + +); + +export const DxBook: FC = ({ children, iconsProps, ...rest }) => { + return ( +
+
+ + + {/*
*/} + {children} +
+ +
+ ); +}; diff --git a/src/components/links.tsx b/src/components/links.tsx new file mode 100644 index 0000000..8f3c3be --- /dev/null +++ b/src/components/links.tsx @@ -0,0 +1,32 @@ +import React, { FC, HTMLAttributes, PropsWithChildren } from "react"; +import { DxIcon } from "@/components/index"; + +export const LinkTargetBlank: FC = ({ href, children }) => ( + + {children} + +); + +export const HhLink = () => ( + + CV at hh.ru + +); + +export const EmailLink = () => ( + + Email + +); + +export const IconLink: FC<{ href: string; className: HTMLAttributes["className"] }> = ({ href, className }) => ( + + + +); + +export const SpbDotNetLink = () => ( + + On SpbDotNet community + +); diff --git a/src/components/styles.module.css b/src/components/styles.module.css new file mode 100644 index 0000000..e69de29