Commit 754cb4e3 authored by Valera Trubachev's avatar Valera Trubachev

initial commit

parents
dist/**
\ No newline at end of file
module.exports = {
extends: [
'airbnb-base',
],
env: {
browser: true,
},
rules: {
'no-param-reassign': [ 'error', { props: false } ],
'indent': [ 'error', 2, { SwitchCase: 0 } ],
}
};
/node_modules
\ No newline at end of file
const path = require('path');
module.exports = {
rootDir: path.resolve(__dirname, '..', 'src'),
collectCoverage: true,
verbose: true,
};
\ No newline at end of file
const path = require('path');
const webpack = require('webpack');
const autoprefixer = require('autoprefixer');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
context: path.resolve(__dirname, '..', 'src'),
entry: {
app: './js/index.js',
},
output: {
path: path.resolve(__dirname, '..', 'dist'),
filename: '[name].[hash].js',
publicPath: '/',
sourceMapFilename: '[name].map',
},
resolve: {
extensions: ['.js'],
modules: [
path.resolve(__dirname, '..', 'src'),
'node_modules',
],
},
module: {
rules: [
{
enforce: 'pre', // to check source files, not modified by other loaders (like babel-loader)
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['env'],
},
},
},
{
test: /\.scss$/,
oneOf: [
{ compiler: 'html-webpack-plugin', loader: 'null-loader' },
{
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'postcss-loader',
options: {
sourceMap: true,
plugins: [
autoprefixer({ browsers: ['last 2 versions'] }),
],
},
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
},
},
],
}),
},
],
},
{
test: /\.(jpe?g|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8192,
name: '[path][name].[ext]',
},
},
{
test: /\.ya?ml$/,
use: [
'json-loader',
'yaml-loader',
],
},
{
test: /\.ejs$/,
loader: 'ejs-loader',
},
],
},
plugins: [
new CleanWebpackPlugin(['dist'], { root: path.resolve(__dirname, '..') }),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
}),
new HtmlWebpackPlugin({
template: 'index.ejs',
}),
new ExtractTextPlugin({
filename: '[name].[contenthash].css',
}),
],
};
const webpack = require('webpack');
const path = require('path');
const merge = require('webpack-merge');
const base = require('./webpack.config.base.js');
module.exports = merge(base, {
devtool: 'source-map',
plugins: [
new webpack.HotModuleReplacementPlugin(),
],
devServer: {
publicPath: '/',
port: 9000,
contentBase: path.resolve(__dirname, '..', 'dist'),
host: 'localhost',
historyApiFallback: true,
noInfo: false,
stats: 'minimal',
hot: true,
},
});
const webpack = require('webpack');
const merge = require('webpack-merge');
const base = require('./webpack.config.base.js');
module.exports = merge(base, {
plugins: [
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false,
}),
new webpack.DefinePlugin({
'process.env': { NODE_ENV: '"production"' },
}),
new webpack.optimize.UglifyJsPlugin({
beautify: false,
mangle: {
screw_ie8: true,
keep_fnames: true,
},
compress: {
screw_ie8: true,
},
comments: false,
}),
],
});
This diff is collapsed.
{
"name": "vetruvet.com",
"version": "1.0.0",
"scripts": {
"start": "webpack-dev-server --config config/webpack.config.dev.js",
"build": "webpack --env=prod --progress --profile --colors",
"lint": "eslint ./src/**.js",
"coverage": "jest --coverage --config=config/jest.config.js",
"test": "jest --config=config/jest.config.js",
"test:watch": "jest --watch"
},
"devDependencies": {
"autoprefixer": "^7.2.3",
"babel-core": "^6.26.0",
"babel-eslint": "^8.0.3",
"babel-jest": "^22.0.3",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1",
"clean-webpack-plugin": "^0.1.17",
"css-loader": "^0.28.5",
"ejs-loader": "^0.3.0",
"eslint": "^4.13.1",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-loader": "^1.9.0",
"eslint-plugin-import": "^2.8.0",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.6",
"html-webpack-plugin": "^2.30.1",
"jest": "^22.0.3",
"json-loader": "^0.5.7",
"node-sass": "^4.7.2",
"null-loader": "^0.1.1",
"postcss-loader": "^2.0.9",
"sass-loader": "^6.0.6",
"style-loader": "^0.20.1",
"url-loader": "^0.6.2",
"webpack": "^3.10.0",
"webpack-dev-server": "^2.9.7",
"webpack-merge": "^4.1.1",
"yaml-loader": "^0.5.0"
},
"dependencies": {
"animated-scroll-to": "^1.1.7",
"normalize.css": "^7.0.0",
"what-input": "^5.0.3"
}
}
This diff is collapsed.
<% const site = require('./site.yml'); %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Valeriy E Trubachev, Full Stack Web Developer</title>
</head>
<body>
<div id="showcase_modal" class="modal" role="dialog" aria-hidden="true">
<button type="button" class="modal__close" aria-label="Close"></button>
<button type="button" class="modal__next" aria-label="Next Image" aria-hidden="true"></button>
<button type="button" class="modal__prev" aria-label="Previous Image" aria-hidden="true"></button>
<div class="modal__body">
<img>
</div>
</div>
<header>
<div class="container">
<h1>Valeriy E Trubachev</h1>
<p>I am a full stack web developer who doesn't like to compromise quality. I use modern tools and languages and try to push the boundary of what is considered best practice. I have pride in my work and like to tackle problems that benefit society as a whole. I pay attention to detail and try to perfect everything I work on.</p>
</div>
<nav>
<ul>
<% Object.entries(site).forEach(([slug, { heading, nav }]) => { %>
<% if (nav !== false) { %>
<li><a href="#<%= slug %>"><%= nav || heading %></a></li>
<% } %>
<% }); %>
</ul>
</nav>
</header>
<main>
<div class="container">
<% Object.entries(site).forEach(([slug, section]) => { %>
<section id="<%= slug %>">
<h2><%= section.heading %></h2>
<% if (section.intro) { %>
<p><%= section.intro.replace(/\n/g, '</p><p>') %></p>
<% } %>
<% if (section.skills && section.skills.length) { %>
<ul class="skills">
<% section.skills.forEach((skill) => { %>
<li><%= skill %></li>
<% }); %>
</ul>
<% } %>
<% if (section.sections && section.sections.length) { %>
<% section.sections.forEach((subsection) => { %>
<h3><%= subsection.heading %></h3>
<% if (subsection.intro) { %>
<p><%= subsection.intro.replace(/\n/g, '</p><p>') %></p>
<% } %>
<% if (subsection.skills && subsection.skills.length) { %>
<ul class="skills">
<% subsection.skills.forEach((skill) => { %>
<li><%= skill %></li>
<% }); %>
</ul>
<% } %>
<% if (subsection.images && subsection.images.length) { %>
<ul class="showcase">
<% subsection.images.forEach(({ src, label }) => { %>
<li><img src="<%= require(`${src}`) %>" alt="<%= label %>"><label><%= label %></label></li>
<% }); %>
</ul>
<% } %>
<% }); %>
<% } %>
</section>
<% }); %>
</div>
</main>
<footer>
<p><small>&copy; 2018 Valeriy E Trubachev.</small></p>
<p>
<a href="https://github.com/vetruvet" target="_blank">GitHub</a>
<a href="https://vetruvet.blogspot.com" target="_blank">Blog</a>
<a href="https://git.vetruvet.com/vetruvet/pseronal-site" target="_blank">Site Source</a>
</p>
</footer>
</body>
</html>
\ No newline at end of file
/* eslint-disable */
window.WebFontConfig = {
google: { families: [ 'Lato:400,700:latin' ] }
};
(function() {
var wf = document.createElement('script');
wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
'://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
wf.type = 'text/javascript';
wf.async = 'true';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(wf, s);
})();
import 'what-input';
import animatedScrollTo from 'animated-scroll-to';
import '../scss/style.scss';
import './font';
function preloadImage(src) {
return new Promise((resolve, reject) => {
const img = document.createElement('img');
img.addEventListener('load', () => {
document.body.removeChild(img);
resolve();
});
img.addEventListener('error', () => {
document.body.removeChild(img);
reject();
});
img.style.display = 'none';
img.src = src;
document.body.appendChild(img);
});
}
function preloadHeroBackground() {
const header = document.querySelector('header');
header.classList.add('back-loaded');
const [, backgroundImage] = /^url\((?:'|")?(.*?)(?:'|")?\)$/i.exec(window.getComputedStyle(header).backgroundImage);
header.classList.remove('back-loaded');
preloadImage(backgroundImage).then(() => header.classList.add('back-loaded'));
}
function bindNavLinks() {
[...document.querySelectorAll('nav a')].forEach((a) => {
a.addEventListener('click', (e) => {
e.preventDefault();
animatedScrollTo(document.getElementById(a.hash.substr(1)).offsetTop - 80);
});
});
}
function bindShowcaseImages() {
const modal = document.getElementById('showcase_modal');
const modalBody = modal.querySelector('.modal__body');
const modalImg = modal.querySelector('img');
const closeModal = () => {
modal.classList.remove('modal--open');
modal.classList.remove('modal--positioned');
modal.setAttribute('aria-hidden', 'true');
};
window.addEventListener('keydown', (e) => {
switch (e.keyCode) { // eslint-disable-line default-case
case 27: // esc
closeModal();
break;
}
});
modal.querySelector('.modal__close').addEventListener('click', closeModal);
modal.addEventListener('click', (e) => {
if (e.target === modal) {
closeModal();
}
});
[...document.querySelectorAll('.showcase li')].forEach((img) => {
img.addEventListener('click', () => {
const {
top, left, width, height,
} = img.getBoundingClientRect();
modalBody.style.top = `${top}px`;
modalBody.style.left = `${left}px`;
modalBody.style.width = `${width}px`;
modalBody.style.height = `${height}px`;
modalImg.src = img.querySelector('img').src;
modal.classList.add('modal--open');
modal.setAttribute('aria-hidden', 'false');
requestAnimationFrame(() => {
requestAnimationFrame(() => {
modalBody.style.top = '';
modalBody.style.left = '';
modalBody.style.width = '';
modalBody.style.height = '';
modal.classList.add('modal--positioned');
});
});
});
});
}
preloadHeroBackground();
bindNavLinks();
bindShowcaseImages();
[data-whatintent="keyboard"] *:focus {
outline: solid 5px green;
}
\ No newline at end of file
main {
background: white;
margin-bottom: 8em;
padding: 2em 0;
h2 {
text-align: center;
font-size: 2em;
}
ul.skills {
padding-left: 2em;
list-style: disc;
}
ul.showcase {
display: flex;
flex-wrap: wrap;
list-style: none;
justify-content: space-around;
align-items: center;
li {
max-width: 15em;
flex: 1 0 10em;
margin: 1em;
position: relative;
transition: transform .125s ease-in-out;
box-shadow: 2px 2px 4px 1px rgba(black, .5);
&:hover {
transform: scale(1.05);
}
&.expand {
position: fixed;
transition: top .25s linear, left .25s linear, bottom .25s linear, right .25s linear;
&--expanded {
top: 10%;
left: 10%;
bottom: 10%;
right: 10%;
}
}
label {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(black, .5);
color: white;
text-align: center;
padding: 0.25em;
}
img {
max-width: 100%;
max-height: 100%;
display: block;
}
}
}
}
footer {
text-align: center;
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: gray;
color: white;
z-index: -1;
padding: 1em;
a {
color: white;
text-decoration: none;
margin: 0 1em;
}
}
body {
font: 16px/1.3 lato, sans-serif;
}
h1, h2, h3 {
margin: 1em 0;
}
p {
padding-bottom: 1em;
}
.container {
max-width: 50em;
margin: 0 auto;
padding: 0 .5em;
header & {
max-width: 30em;
z-index: 1;
}
}
header {
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: gray;
color: white;
text-align: center;
position: relative;
box-shadow: 0 2px 4px 2px rgba(black, .5);
@media screen and (min-width: 37.5em) {
position: sticky;
top: calc(-100vh + 4em);
z-index: 99;
}
&::before {
content: " ";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 1;
background: darken(gray, 15%);
transition: opacity 0.5s ease-in-out;
}
&.back-loaded {
background: url('../img/hard-drive.jpg') no-repeat center center;
background-size: cover;
&:before {
opacity: 0.75;
}
}
h1 {
font-size: 7.5vw;
}
nav {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 0.5em 0;
ul {
display: flex;
list-style: none;
justify-content: center;
flex-wrap: wrap;
li {
padding: 1em;
a {
text-decoration: none;
text-transform: uppercase;
color: white;
position: relative;
padding: .25em;
&::after {
content: " ";
position: absolute;
width: 100%;
height: 3px;
left: 0;
top: 100%;
background: white;
transform: scaleX(0);
transition: transform .2s ease-in-out;
}
&:hover::after {
transform: scaleX(1);
}
}
}
}
}
}
%button {
position: absolute;
width: 2em;
height: 2em;
background: rgba(white, .5);
padding: 1em;
font-size: 1.25em;
border-radius: 4px;
}
%line {
content: " ";
position: absolute;
top: calc(1em - 2px);
width: 1em;
height: 4px;
background: black;
}
%line--left {
left: .5em;
}
%line--right {
right: .5em;
}
.modal {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
display: none;
background: transparent;
transition: background .25s ease-in-out;
z-index: 9999;
&--open {
display: block;
background: rgba(black, .6);
}