homepage/index.php
2026-05-18 21:42:25 +02:00

291 lines
10 KiB
PHP

<?php
/**
* Content config:
*/
$name = "David Hrdina Němeček";
$site_title = "www.dejvino.cz";
$site_url = "https://www.dejvino.cz/";
$site_desc = "$name - Software Developer";
$subtitle = "Software Developer, Architect, Problem Solver";
$photo_file = "photo.png";
$email_user = "ciao";
$about_text = [
"Full-stack developer focused on pragmatic architecture, observability and performance.",
//""
];
$nav_links = [
"~/blog" => "https://wp.dejvino.com/",
"~/projects" => "https://projects.dejvino.com/",
"~/code" => "https://git.dejvino.cz/",
"~/cv" => "#",
];
$hardware_info = "Playing the mandolin, mountain biking, 3D printing and tinkering.";
$extra_contact_links = [
"LinkedIn" => "https://www.linkedin.com/in/hrdinadavid/",
"GitHub" => "https://github.com/dejvino",
"StackOverflow" => "https://stackoverflow.com/users/3551523/dejvino",
];
/**
* CV Data
*/
$cv_experience = [
[
"title" => "Senior Software Engineer / Architect",
"org" => "Self-Employed / Various Projects",
"date" => "2018 - Present",
"desc" => "Designing and implementing scalable backend architectures, focusing on performance, observability, and clean code principles."
],
[
"title" => "Full-stack Developer",
"org" => "Tech Innovations s.r.o.",
"date" => "2014 - 2018",
"desc" => "Built complex web applications using PHP and modern JavaScript frameworks, optimized database performance."
]
];
$cv_education = [
[ "title" => "Master of Computer Science", "org" => "Technical University", "date" => "2014", "desc" => "" ]
];
$cv_skills = ["PHP", "Go", "Rust", "Docker", "Kubernetes", "PostgreSQL", "Redis", "Linux", "Git"];
/**
* Page design:
* - clean and simple, human-maintaninable
* - no dependencies
* - single-file website
*/
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title><?= $site_title ?></title>
<link rel="canonical" href="<?= $site_url ?>">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="<?= $site_desc ?>">
<meta name="author" content="<?= $name ?>">
<!-- Open Graph / Social Media -->
<meta property="og:type" content="website">
<meta property="og:url" content="<?= $site_url ?>">
<meta property="og:title" content="<?= $name ?>">
<meta property="og:description" content="<?= $subtitle ?>">
<style>
<?php
include 'style.css';
?>
</style>
<link rel="shortcut icon" href="favicon.ico">
</head>
<body>
<main class="bento-grid">
<section class="card profile">
<?php
if (file_exists($photo_file)) {
$avatarBase64 = base64_encode(file_get_contents($photo_file));
echo '<img src="data:image/png;base64,' . $avatarBase64 . '" alt="' . $name . '" class="avatar" width="120" height="160" />';
} else {
echo '<div class="avatar-placeholder"></div>';
}
?>
<div class="header-text">
<h1><?= $name ?></h1>
<p class="subtitle"><?= $subtitle ?></p>
</div>
</section>
<section class="card about">
<h2>Bio</h2>
<?php foreach ($about_text as $para): ?>
<p><?= $para ?></p>
<?php endforeach; ?>
</section>
<section class="card interests">
<h2>Likes</h2>
<p><?= $hardware_info ?></p>
</section>
<section class="card contact">
<h2>Remote_Access</h2>
<p class="mono-addr">
Contact email: <span id="contact-mail">uncovering...</span>
</p>
<ul class="link-grid cv-only hidden">
<?php foreach ($extra_contact_links as $label => $url): ?>
<li><a href="<?= $url ?>" target="_blank"><span><?= $label ?></span></a></li>
<?php endforeach; ?>
</ul>
</section>
<section class="card experience hidden">
<h2>Experience</h2>
<?php foreach ($cv_experience as $job): ?>
<div class="cv-item">
<div class="cv-header">
<span class="cv-title"><?= $job['title'] ?></span>
<span class="cv-date"><?= $job['date'] ?></span>
</div>
<div class="cv-org"><?= $job['org'] ?></div>
<div class="cv-desc"><?= $job['desc'] ?></div>
</div>
<?php endforeach; ?>
</section>
<section class="card skills hidden">
<h2>Skills</h2>
<div class="skill-tags">
<?php foreach ($cv_skills as $skill): ?>
<span class="tag"><?= $skill ?></span>
<?php endforeach; ?>
</div>
</section>
<section class="card education hidden">
<h2>Education</h2>
<?php foreach ($cv_education as $edu): ?>
<div class="cv-item">
<div class="cv-header">
<span class="cv-title"><?= $edu['title'] ?></span>
<span class="cv-date"><?= $edu['date'] ?></span>
</div>
<div class="cv-org"><?= $edu['org'] ?></div>
<?php if ($edu['desc']): ?>
<div class="cv-desc"><?= $edu['desc'] ?></div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</section>
<section class="card links">
<h2>Navigation</h2>
<ul class="link-grid nav-default">
<?php foreach ($nav_links as $label => $url):
$id_attr = ($url === "#") ? 'id="cv-toggle"' : '';
?>
<li><a href="<?= $url ?>" <?= $id_attr ?>><span><?= $label ?></span></a></li>
<?php endforeach; ?>
</ul>
<ul class="link-grid cv-only hidden">
<li><a href="#" id="cv-back"><span>GO BACK ~/</span></a></li>
</ul>
</section>
</main>
<script>
((u, d, t) => {
const addr = u + '@' + d + '.' + t;
const el = document.getElementById("contact-mail");
el.innerHTML = `<a href="mailto:${addr}">${addr}</a>`;
})('<?= $email_user ?>', 'dejvino', 'cz');
const animationDuration = 1000; // Matches CSS animation duration (1s)
const staggerDelay = 200; // Delay between each panel unrolling (ms)
const applyShifting = (el, flashAtEnd = true, delay = animationDuration) => {
if (!el) return;
const card = el.classList.contains('card') ? el : el.closest('.card');
if (card && !card.dataset.isShifting) {
card.dataset.isShifting = "true";
card.classList.add('shifting');
setTimeout(() => {
card.classList.remove('shifting', 'reveal', 'fade-out');
delete card.dataset.isShifting;
if (!flashAtEnd) return;
card.classList.add('flash-glow');
setTimeout(() => card.classList.remove('flash-glow'), 600);
}, delay);
}
};
const toggleCV = (show, updateHash = true) => {
const grid = document.querySelector('.bento-grid');
const cvPanels = [
document.querySelector('.experience'),
document.querySelector('.skills'),
document.querySelector('.education'),
...document.querySelectorAll('.cv-only')
].filter(el => el);
const navDefault = document.querySelectorAll('.nav-default');
if (show) {
grid.classList.add('cv-mode');
// Hide default navigation
navDefault.forEach(el => {
el.classList.remove('reveal');
el.classList.add('fade-out');
applyShifting(el, false); // No flash when disappearing
setTimeout(() => el.classList.add('hidden'), animationDuration);
});
// Unroll CV panels sequentially (Top -> Bottom)
cvPanels.forEach((el, index) => {
setTimeout(() => {
el.classList.remove('hidden', 'fade-out');
el.classList.add('reveal');
applyShifting(el, true); // Flash when appearing
}, index * staggerDelay);
});
} else {
// Roll up CV panels sequentially (Bottom -> Top)
[...cvPanels].reverse().forEach((el, index) => {
setTimeout(() => {
el.classList.remove('reveal');
el.classList.add('fade-out');
applyShifting(el, false); // No flash when disappearing
setTimeout(() => {
el.classList.add('hidden');
el.classList.remove('fade-out');
}, animationDuration);
}, index * staggerDelay);
});
// Restore default state after panels have started rolling up
setTimeout(() => {
grid.classList.remove('cv-mode');
navDefault.forEach(el => {
el.classList.remove('hidden', 'fade-out');
el.classList.add('reveal');
applyShifting(el, true); // Flash when navigation returns
});
}, cvPanels.length * staggerDelay);
}
if (updateHash) {
window.location.hash = show ? 'CV' : '';
}
};
// Initialize state from URL
const checkHash = () => toggleCV(window.location.hash === '#CV', false);
window.addEventListener('load', checkHash);
window.addEventListener('hashchange', checkHash);
document.getElementById('cv-toggle')?.addEventListener('click', (e) => {
e.preventDefault();
toggleCV(true);
});
document.getElementById('cv-back')?.addEventListener('click', (e) => {
e.preventDefault();
toggleCV(false);
window.scrollTo({ top: 0, behavior: 'smooth' });
});
</script>
</body>
</html>