6 Commits

Author SHA1 Message Date
4e367d73a0 order projects
Some checks failed
test / cargo test (push) Failing after 57s
2024-09-27 13:34:07 +02:00
85c98fac56 showcase page
Some checks failed
test / cargo test (push) Failing after 1m12s
2024-09-27 11:52:25 +02:00
f62673d6a7 yutube and instagram
Some checks failed
test / cargo test (push) Failing after 59s
2024-09-26 14:59:18 +02:00
221d5cef23 twitch and tiktok hover effects
Some checks failed
test / cargo test (push) Failing after 1m5s
2024-09-26 14:12:55 +02:00
dc1a01c352 yes yes yes
Some checks failed
test / cargo test (push) Failing after 1m6s
2024-09-26 12:19:45 +02:00
20d1314925 lol hide instagram between lg-xl
Some checks failed
test / cargo test (push) Failing after 1m12s
2024-09-25 16:21:35 +02:00
60 changed files with 624 additions and 301 deletions

View File

@ -117,7 +117,7 @@ For example this is the model of this blog post:
Neat part of the _CMS_ are those widgets. In editor they will be presented by appropriate component as well as in the editor preview.
I am very satisfied with it and I recommend it.
## What's next
## What's next {#whats-next}
I've decided not to wait for perfect product and I want to release this blog as soon as possible. I will have same approach as with other products. Make a <abbr title="Minimum Viable Product">**MVP**</abbr> and then release features as they are done.
I've put some features into _Github Projects Board_. I will very likely make a redesign with experimental layout changes. I'd like to experiment with colors and make the blog look distinguishable and personal while maintaining accessibility.

View File

@ -28,7 +28,7 @@ This week I've attended a [Rusty game jam #2](https://itch.io/jam/rusty-jam-2).
![Egg fetcher game preview](/images/uploads/screenshot-from-2022-06-26-22-37-16.png "Egg fetcher game preview")
[You can check the rusult built with WASM here.](/showcase/egg-fetcher/)
[You can check the result built with WASM here.](/showcase/egg-fetcher/)
## What's up with the weeklys

View File

@ -1,11 +1,12 @@
---
title: CK Vive
displayed: true
description: Websitefor *CK Vive* travel agency with a **custom CMS system** for
managing travel destinations.
cover_image: /images/uploads/ck_vive_logo.svg
link: https://ckvive.sk/
classification: website
tags:
- PHP
featured: false
---
Websitefor *CK Vive* travel agency with a **custom CMS system** for
managing travel destinations.

View File

@ -1,8 +1,6 @@
---
title: Košice Peace Marathon
displayed: true
description: "*Košice Peace Marathon* is the oldest marathon in Europe and the
third-oldest in the world."
link: https://www.kosicemarathon.com/
cover_image: /images/uploads/screenshot-from-2024-08-06-18-22-52.png
classification: website
@ -11,3 +9,5 @@ tags:
- MySQL
featured: false
---
*Košice Peace Marathon* is the oldest marathon in Europe and the
third-oldest in the world.

View File

@ -1,8 +1,6 @@
---
title: Docker
displayed: false
description: An introduction to Docker containerization technology and how it
differs from virtualization.
cover_image: /images/uploads/docker-use-cases.png
classification: presentation
tags:
@ -10,3 +8,5 @@ tags:
- Docker
featured: false
---
An introduction to Docker containerization technology and how it
differs from virtualization.

View File

@ -0,0 +1,13 @@
---
title: Unstoppable growth of front-end frameworks
displayed: true
link: https://michalvankodev.github.io/unstoppable-growth-of-frontend-frameworks/
classification: presentation
tags:
- Presentation
- NodeJS
featured: false
---
A simple summary of the web front-end evolution. Describes how and
why new tools in the NodeJS ecosystem improve & why there is still something
to explore.

View File

@ -0,0 +1,10 @@
---
title: Skosy
displayed: false
classification: webapp
tags:
- Webapp
featured: false
---
*Skosy* is a web application whose purpose is to **automate the
writing of integration tests** for websites.

View File

@ -0,0 +1,13 @@
---
title: beinSports
displayed: true
link: https://www.beinsports.com/en-us
cover_image: /images/uploads/bein_logo.af017869.webp
classification: website
tags:
- Freemarker
featured: false
---
*beIN Sports* is a global network of sports channels jointly owned
and operated by *Qatari Sports Investments*, an affiliate of *Al Jazeera Media
Networks*

View File

@ -0,0 +1,10 @@
---
title: Livesport.tv
displayed: false
classification: website
tags:
- Freemarker
featured: false
---
*Livesport.tv* is a network of premium online sports channels,
featuring all the top sports competitions from around the world.

View File

@ -0,0 +1,14 @@
---
title: Spreading the Web
displayed: true
link: https://michalvankodev.github.io/spreading-the-web
cover_image: /images/uploads/screenshot-from-2024-08-06-18-48-02.png
classification: presentation
tags:
- Presentation
- NodeJS
featured: false
---
A presentation about the rising number of use cases for utilizing
web technologies outside of the web platform such as native mobile
applications and robotics. 2015

View File

@ -0,0 +1,15 @@
---
title: FX Sales
displayed: true
link: https://www.caplin.com/business/fx-sales
cover_image: /images/uploads/fx_sales_screen2x.png
classification: webapp
tags:
- Webapp
- React
- Knockout
featured: false
---
*Caplin FX Sales* allows sales people to **trade on behalf of
their clients**. This needs to be an efficient workflow providing all the
relevant information to the sales user

View File

@ -1,11 +1,11 @@
---
title: SHIP (Structured heard input process)
displayed: true
description: "*SHIP* is a web application for **editors** who actively **track
trades offers and bids** on the commodity market."
classification: webapp
tags:
- Webapp
- Angular
featured: false
---
*SHIP* is a web application for **editors** who actively **track
trades offers and bids** on the commodity market.

View File

@ -0,0 +1,15 @@
---
title: responzIO
displayed: true
link: https://www.croptech.com/
cover_image: /images/uploads/responzio.png
classification: embedded
tags:
- Webapp
- Embedded
- NodeJS
featured: false
---
***responzIO*** is a smart, easy-to-use monitoring and automation
system. The ultimate tool for various applications such as hydroponics,
aquariums, and gardens.

View File

@ -0,0 +1,14 @@
---
title: Manualogic
displayed: false
classification: webapp
tags:
- Webapp
- Angular
- RxJS
featured: false
---
*Manualogic* is a **single-page application** for product manual
creators. It contains **custom web editor** and management system of
**translatable pages, books** and **products.** Its main goal is to enable
customers to get manuals of their products in digital form.

View File

@ -0,0 +1,13 @@
---
title: Signal Hub Manager
displayed: true
classification: webapp
tags:
- Webapp
- React
featured: false
---
*Signal Hub* is an end-to-end **Big Data analytics platform** for
large enterprises. It accelerates the process of extracting insights and
intelligence from large volumes of data, including data of different types and
in different formats.

View File

@ -1,9 +1,6 @@
---
title: Panoramic
displayed: true
description: "*Panoramic* was a company focused on building a web application
for data scientists to be able to create and share models and graphs in
between each other."
classification: webapp
tags:
- Webapp
@ -14,3 +11,6 @@ tags:
- Data analytics
featured: false
---
*Panoramic* was a company focused on building a web application
for data scientists to be able to create and share models and graphs in
between each other.

View File

@ -1,11 +1,6 @@
---
title: The Expert
displayed: true
description: _The Expert_ is a digital platform that connects clients to
interior designers around the world. For experts, it allows **managing** their
**portfolio and profile** and **schedule** in which they are open for
**consultations**. Clients are able to view their profiles and book
consultations.
link: https://www.theexpert.com/
cover_image: /images/uploads/the-expert-logo.svg
classification: webapp
@ -17,3 +12,8 @@ tags:
- GraphQL
featured: true
---
_The Expert_ is a digital platform that connects clients to
interior designers around the world. For experts, it allows **managing** their
**portfolio and profile** and **schedule** in which they are open for
**consultations**. Clients are able to view their profiles and book
consultations.

View File

@ -1,8 +1,7 @@
---
title: WebAssembly presentation
displayed: true
description: A presentation about what WebAssembly is about and how it might
affect the future of the world.
link: https://michalvankodev.github.io/presentation-webassembly/
cover_image: /images/uploads/screenshot-from-2024-08-06-18-52-41.png
classification: presentation
tags:
@ -10,3 +9,5 @@ tags:
- WebAssembly
featured: false
---
A presentation about what WebAssembly is about and how it might
affect the future of the world.

View File

@ -0,0 +1,14 @@
---
title: The Grand Escape
displayed: true
link: https://michalvankodev.itch.io/the-grand-escape
cover_image: /images/uploads/logo.png
classification: videogame
tags:
- Rust
- Bevy
featured: true
---
A videogame where you need to steer your boat to avoid obstacles
and enemy bullets. The difficulty will be increased after a certain time and
new enemies will be spawned to make your escape harder.

View File

@ -1,8 +1,6 @@
---
title: Renaissance of hypermedia systems
displayed: true
description: A presentation about hypermedia systems, HTMX, HyperView, and the
HATEOAS principles. 2024
link: https://michalvankodev.github.io/presentation-renaissance-of-hypermedia-systems/#/intro
cover_image: /images/uploads/screenshot-from-2024-08-06-19-01-03.png
classification: presentation
@ -12,3 +10,5 @@ tags:
- HTMX
featured: true
---
A presentation about hypermedia systems, HTMX, HyperView, and the
HATEOAS principles. 2024

View File

@ -1,12 +0,0 @@
---
title: beinSports
displayed: true
description: "*beIN Sports* is a global network of sports channels jointly owned
and operated by *Qatari Sports Investments*, an affiliate of *Al Jazeera Media
Networks*"
cover_image: /images/uploads/bein_logo.af017869.webp
classification: website
tags:
- Freemarker
featured: false
---

View File

@ -1,9 +1,9 @@
---
title: dev project test
displayed: true
description: Testing project
displayed: false
classification: webapp
tags:
- Webapp
featured: false
---
Testing project

View File

@ -1,14 +0,0 @@
---
title: FX Sales
displayed: true
description: "*Caplin FX Sales* allows sales people to **trade on behalf of
their clients**. This needs to be an efficient workflow providing all the
relevant information to the sales user"
cover_image: /images/uploads/fx_sales_screen2x.png
classification: webapp
tags:
- Webapp
- React
- Knockout
featured: false
---

View File

@ -1,8 +1,7 @@
---
title: HeyLady!
displayed: true
description: A thriving online community supporting women to learn and practise
speaking English.
link: https://www.heylady.io/
cover_image: /images/uploads/heyladylogo.svg
classification: webapp
tags:
@ -14,3 +13,5 @@ tags:
- PostgreSQL
featured: false
---
A thriving online community supporting women to learn and practise
speaking English.

View File

@ -1,10 +0,0 @@
---
title: Livesport.tv
displayed: false
description: "*Livesport.tv* is a network of premium online sports channels,
featuring all the top sports competitions from around the world."
classification: website
tags:
- Freemarker
featured: false
---

View File

@ -1,14 +0,0 @@
---
title: Manualogic
displayed: false
description: "*Manualogic* is a **single-page application** for product manual
creators. It contains **custom web editor** and management system of
**translatable pages, books** and **products.** Its main goal is to enable
customers to get manuals of their products in digital form."
classification: webapp
tags:
- Webapp
- Angular
- RxJS
featured: false
---

View File

@ -1,14 +0,0 @@
---
title: responzIO
displayed: true
description: "***responzIO*** is a smart, easy-to-use monitoring and automation
system. The ultimate tool for various applications such as hydroponics,
aquariums, and gardens."
cover_image: /images/uploads/responzio.png
classification: embedded
tags:
- Webapp
- Embedded
- NodeJS
featured: false
---

View File

@ -1,13 +0,0 @@
---
title: Signal Hub Manager
displayed: true
description: "*Signal Hub* is an end-to-end **Big Data analytics platform** for
large enterprises. It accelerates the process of extracting insights and
intelligence from large volumes of data, including data of different types and
in different formats."
classification: webapp
tags:
- Webapp
- React
featured: false
---

View File

@ -1,10 +0,0 @@
---
title: Skosy
displayed: false
description: "*Skosy* is a web application whose purpose is to **automate the
writing of integration tests** for websites."
classification: webapp
tags:
- Webapp
featured: false
---

View File

@ -1,13 +0,0 @@
---
title: Spreading the Web
displayed: true
description: A presentation about the rising number of use cases for utilizing
web technologies outside of the web platform such as native mobile
applications and robotics. 2015
cover_image: /images/uploads/screenshot-from-2024-08-06-18-48-02.png
classification: presentation
tags:
- Presentation
- NodeJS
featured: false
---

View File

@ -1,14 +0,0 @@
---
title: The Grand Escape
displayed: true
description: A videogame where you need to steer your boat to avoid obstacles
and enemy bullets. The difficulty will be increased after a certain time and
new enemies will be spawned to make your escape harder.
link: https://michalvankodev.itch.io/the-grand-escape
cover_image: /images/uploads/logo.png
classification: videogame
tags:
- Rust
- Bevy
featured: true
---

View File

@ -1,12 +0,0 @@
---
title: Unstoppable growth of front-end frameworks
displayed: true
description: A simple summary of the web front-end evolution. Describes how and
why new tools in the NodeJS ecosystem improve & why there is still something
to explore.
classification: presentation
tags:
- Presentation
- NodeJS
featured: false
---

View File

@ -35,7 +35,7 @@ rustflags = ["-Z", "threads=8"]
# ]
[profile.dev]
debug = false
debug = true
opt-level = 0
# codegen-units = 16
# lto = "thin"

View File

@ -46,7 +46,7 @@ wait_for_port:
# Kill the application running on port
kill:
kill $(lsof -t -i:{{port}})
kill $(pidof axum_server)
# Clean the dist folder
clean:

View File

@ -3,3 +3,4 @@ pub mod blog_post_list;
pub mod blog_post_page;
pub mod contact;
pub mod index;
pub mod project_list;

View File

@ -0,0 +1,30 @@
use askama::Template;
use axum::http::StatusCode;
use crate::{
components::site_header::HeaderProps,
post_utils::{post_listing::get_post_list, post_parser::ParseResult},
projects::project_model::ProjectMetadata,
};
#[derive(Template)]
#[template(path = "project_list.html")]
pub struct ProjectListTemplate {
pub title: String,
pub project_list: Vec<ParseResult<ProjectMetadata>>,
pub header_props: HeaderProps,
}
pub async fn render_projects_list() -> Result<ProjectListTemplate, StatusCode> {
let mut project_list = get_post_list::<ProjectMetadata>("../_projects").await?;
project_list.sort_by_key(|post| post.slug.to_string());
project_list.retain(|project| project.metadata.displayed);
project_list.reverse();
Ok(ProjectListTemplate {
title: "Showcase".to_owned(),
header_props: HeaderProps::default(),
project_list,
})
}

View File

@ -1,3 +1,4 @@
use core::panic;
use std::path::Path;
use axum::http::StatusCode;
@ -9,7 +10,7 @@ use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd};
use serde::{de::DeserializeOwned, Deserialize, Deserializer};
use syntect::{highlighting::ThemeSet, html::highlighted_html_for_string, parsing::SyntaxSet};
use tokio::fs;
use tracing::debug;
use tracing::{debug, error, info};
use crate::picture_generator::{
picture_markup_generator::generate_picture_markup, resolutions::get_max_resolution,
@ -89,6 +90,7 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
let syntax_set = SyntaxSet::load_defaults_newlines();
let theme_set = ThemeSet::load_defaults();
let theme = theme_set.themes.get("InspiredGitHub").unwrap();
let mut heading_ended: Option<bool> = None;
let parser = Parser::new_ext(markdown, options).map(|event| match event {
/*
@ -181,14 +183,24 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
text.to_lowercase()
.replace(|c: char| !c.is_alphanumeric(), "-")
});
Event::Html(
formatdoc!(
r##"id="{heading_id}">
{text}
"##
)
.into(),
)
debug!("heading_id: {}", heading_id.clone());
match heading_ended {
None => {
error!("Heading should have set state");
panic!("Heading should have set state");
}
Some(true) => Event::Html(text),
Some(false) => {
heading_ended = Some(true);
Event::Html(
formatdoc!(
r##"id="{heading_id}">
{text}"##
)
.into(),
)
}
}
}
_ => Event::Text(text),
},
@ -199,7 +211,9 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
attrs: _,
}) => {
let id_str = id.map(|id| id.to_string());
debug!("heading_start: {:?}, level: {}", &id_str, level);
text_kind = TextKind::Heading(id_str);
heading_ended = Some(false);
Event::Html(format!("<{level} ").into())
}
Event::Start(_) => event,
@ -210,6 +224,7 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
}
Event::End(TagEnd::Heading(heading_level)) => {
text_kind = TextKind::Text;
heading_ended = None;
Event::End(TagEnd::Heading(heading_level))
}
_ => event,

View File

@ -1,9 +1,6 @@
use axum::http::StatusCode;
use crate::post_utils::{
post_listing::get_post_list,
post_parser::{parse_html, ParseResult},
};
use crate::post_utils::{post_listing::get_post_list, post_parser::ParseResult};
use super::project_model::ProjectMetadata;
@ -13,10 +10,6 @@ pub async fn get_featured_projects() -> Result<Vec<ParseResult<ProjectMetadata>>
let featured_projects = project_list
.into_iter()
.filter(|post| post.metadata.featured)
.map(|mut post| {
post.metadata.description = parse_html(&post.metadata.description, false);
post
})
.collect();
Ok(featured_projects)

View File

@ -3,7 +3,6 @@ use serde::Deserialize;
#[derive(Deserialize, Debug)]
pub struct ProjectMetadata {
pub title: String,
pub description: String,
pub classification: String,
pub displayed: bool,
pub cover_image: Option<String>,
@ -18,6 +17,7 @@ pub fn translate_classification(classification: &str) -> &str {
"website" => "Web site",
"presentation" => "Presentation",
"videogame" => "Video game",
"embedded" => "Embedded system",
any => any,
}
}

View File

@ -3,6 +3,7 @@ use crate::{
pages::{
admin::render_admin, blog_post_list::render_blog_post_list,
blog_post_page::render_blog_post, contact::render_contact, index::render_index,
project_list::render_projects_list,
},
};
use axum::{extract::MatchedPath, http::Request, routing::get, Router};
@ -16,6 +17,7 @@ pub fn get_router() -> Router {
.route("/blog/tags/:tag", get(render_blog_post_list))
.route("/blog/:post_id", get(render_blog_post))
.route("/contact", get(render_contact))
.route("/showcase", get(render_projects_list))
.route("/admin", get(render_admin))
.route("/feed.xml", get(render_rss_feed))
.layer(

View File

@ -150,7 +150,18 @@ strong {
ul,
ol {
@apply px-4 my-2 text-slate-950 mx-auto max-w-read md:text-lg md:my-8 lg:text-readxl;
@apply pl-10 pr-6 my-2 text-slate-950 mx-auto max-w-read md:text-lg md:my-8 lg:text-readxl lg:pl-14;
& p {
@apply px-2;
}
}
ul {
@apply list-disc;
}
ol {
@apply list-decimal;
}
iframe {
@ -165,3 +176,115 @@ article a {
.video-embed {
@apply m-4 mx-auto max-w-image aspect-video;
}
.social-card-twitch:hover {
transform: translate3d(0.6rem, -0.6rem, 0px);
box-shadow: -3px 3px 0px 3px #6441a5;
transition-delay: 75ms;
}
.social-card-youtube:hover {
@apply rounded-none;
transform: scale(1.02);
transition-delay: 100ms;
}
.social-card-instagram:hover {
filter: brightness(84%);
transition-delay: 100ms;
}
.social-card-tiktok {
position: relative;
&:hover {
animation: tiktok-glitch 1.5s infinite;
animation-delay: 200ms;
}
}
@keyframes tiktok-glitch {
0% {
box-shadow:
0px 0px 0 rgba(0, 255, 255, 0),
0px 0px 0 rgba(255, 0, 255, 0);
transform: translate(0, 0);
}
10% {
box-shadow:
-3px -3px 0 rgba(0, 255, 255, 0.8),
3px 3px 0 rgba(255, 0, 255, 0.8);
transform: translate(-1px, -1px);
}
15% {
box-shadow:
2px -2px 0 rgba(0, 255, 255, 0.6),
-2px 2px 0 rgba(255, 0, 255, 0.6);
transform: translate(2px, -2px);
}
20% {
box-shadow:
-1px 1px 0 rgba(0, 255, 255, 0.4),
1px -1px 0 rgba(255, 0, 255, 0.4);
transform: translate(1px, 1px);
}
25% {
box-shadow:
-4px 4px 0 rgba(0, 255, 255, 1),
4px -4px 0 rgba(255, 0, 255, 1);
transform: translate(-2px, 2px);
}
30% {
box-shadow:
3px -3px 0 rgba(0, 255, 255, 0.5),
-3px 3px 0 rgba(255, 0, 255, 0.5);
transform: translate(3px, -3px);
}
40% {
box-shadow:
-2px 2px 0 rgba(0, 255, 255, 0.9),
2px -2px 0 rgba(255, 0, 255, 0.9);
transform: translate(-1px, 1px);
}
50% {
box-shadow:
-1px -2px 0 rgba(0, 255, 255, 0.7),
2px -1px 0 rgba(255, 0, 255, 0.7);
transform: translate(1px, -1px);
}
60% {
box-shadow:
2px -2px 0 rgba(0, 255, 255, 0.3),
-2px 2px 0 rgba(255, 0, 255, 0.3);
transform: translate(2px, -2px);
}
75% {
box-shadow:
-3px 3px 0 rgba(0, 255, 255, 1),
3px -3px 0 rgba(255, 0, 255, 1);
transform: translate(-3px, 3px);
}
85% {
box-shadow:
-2px -2px 0 rgba(0, 255, 255, 0.2),
2px 2px 0 rgba(255, 0, 255, 0.2);
transform: translate(-2px, -2px);
}
100% {
box-shadow:
0px 0px 0 rgba(0, 255, 255, 0),
0px 0px 0 rgba(255, 0, 255, 0);
transform: translate(0, 0);
}
}

View File

@ -658,18 +658,10 @@ video {
float: inline-start;
}
.clear-left {
clear: left;
}
.clear-both {
clear: both;
}
.m-0 {
margin: 0px;
}
.m-1 {
margin: 0.25rem;
}
@ -682,6 +674,10 @@ video {
margin: 1.25rem;
}
.m-6 {
margin: 1.5rem;
}
.mx-0\.5 {
margin-left: 0.125rem;
margin-right: 0.125rem;
@ -717,6 +713,16 @@ video {
margin-bottom: 0.5rem;
}
.my-3 {
margin-top: 0.75rem;
margin-bottom: 0.75rem;
}
.my-4 {
margin-top: 1rem;
margin-bottom: 1rem;
}
.my-5 {
margin-top: 1.25rem;
margin-bottom: 1.25rem;
@ -734,6 +740,10 @@ video {
margin-bottom: 0.75rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.mb-5 {
margin-bottom: 1.25rem;
}
@ -742,34 +752,14 @@ video {
margin-bottom: 1.5rem;
}
.mt-3 {
margin-top: 0.75rem;
}
.ml-1 {
margin-left: 0.25rem;
}
.mr-1 {
margin-right: 0.25rem;
}
.mr-2 {
margin-right: 0.5rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.mr-4 {
margin-right: 1rem;
}
.mr-3 {
margin-right: 0.75rem;
}
.mt-3 {
margin-top: 0.75rem;
}
.block {
display: block;
}
@ -794,10 +784,6 @@ video {
display: none;
}
.aspect-video {
aspect-ratio: 16 / 9;
}
.h-12 {
height: 3rem;
}
@ -806,10 +792,6 @@ video {
height: 1.5rem;
}
.h-64 {
height: 16rem;
}
.h-7 {
height: 1.75rem;
}
@ -822,10 +804,6 @@ video {
height: auto;
}
.h-\[236px\] {
height: 236px;
}
.max-h-\[236px\] {
max-height: 236px;
}
@ -850,15 +828,14 @@ video {
width: 180px;
}
.w-fit {
width: -moz-fit-content;
width: fit-content;
}
.max-w-\[32rem\] {
max-width: 32rem;
}
.max-w-\[392px\] {
max-width: 392px;
}
.max-w-maxindex {
max-width: 100rem;
}
@ -867,10 +844,6 @@ video {
max-width: 64rem;
}
.max-w-xl {
max-width: 36rem;
}
.flex-grow {
flex-grow: 1;
}
@ -885,16 +858,8 @@ video {
break-inside: avoid;
}
.grid-flow-col {
grid-auto-flow: column;
}
.grid-cols-\[max-content_1fr\] {
grid-template-columns: max-content 1fr;
}
.grid-rows-\[max-content_1fr_max-content\] {
grid-template-rows: max-content 1fr max-content;
.grid-flow-row {
grid-auto-flow: row;
}
.flex-row {
@ -933,8 +898,8 @@ video {
gap: 0.5rem;
}
.gap-4 {
gap: 1rem;
.gap-6 {
gap: 1.5rem;
}
.self-start {
@ -945,10 +910,6 @@ video {
align-self: center;
}
.overflow-hidden {
overflow: hidden;
}
.rounded {
border-radius: 0.25rem;
}
@ -961,6 +922,10 @@ video {
border-radius: 0.375rem;
}
.rounded-sm {
border-radius: 0.125rem;
}
.border {
border-width: 1px;
}
@ -1012,10 +977,6 @@ video {
fill: #0b2746;
}
.p-0 {
padding: 0px;
}
.p-0\.5 {
padding: 0.125rem;
}
@ -1760,8 +1721,11 @@ strong {
}
ul,
ol {
padding-left: 1rem;
padding-right: 1rem;
padding-left: 2.5rem;
}
ul,
ol {
padding-right: 1.5rem;
}
ul,
ol {
@ -1782,6 +1746,12 @@ strong {
line-height: 1.75rem;
}
}
@media (min-width: 1024px) {
ul,
ol {
padding-left: 3.5rem;
}
}
@media (min-width: 1024px) {
ul,
ol {
@ -1791,6 +1761,19 @@ strong {
font-weight: 400;
}
}
ul,
ol {
& p {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
}
ul {
list-style-type: disc;
}
ol {
list-style-type: decimal;
}
iframe {
margin-left: auto;
margin-right: auto;
@ -1822,6 +1805,121 @@ article a:visited {
max-width: min(70rem, 95vw);
}
.social-card-twitch:hover {
transform: translate3d(0.6rem, -0.6rem, 0px);
box-shadow: -3px 3px 0px 3px #6441a5;
transition-delay: 75ms;
}
.social-card-youtube:hover {
border-radius: 0px;
transform: scale(1.02);
transition-delay: 100ms;
}
.social-card-instagram:hover {
filter: brightness(84%);
transition-delay: 100ms;
}
.social-card-tiktok {
position: relative;
&:hover {
animation: tiktok-glitch 1.5s infinite;
animation-delay: 200ms;
}
}
@keyframes tiktok-glitch {
0% {
box-shadow:
0px 0px 0 rgba(0, 255, 255, 0),
0px 0px 0 rgba(255, 0, 255, 0);
transform: translate(0, 0);
}
10% {
box-shadow:
-3px -3px 0 rgba(0, 255, 255, 0.8),
3px 3px 0 rgba(255, 0, 255, 0.8);
transform: translate(-1px, -1px);
}
15% {
box-shadow:
2px -2px 0 rgba(0, 255, 255, 0.6),
-2px 2px 0 rgba(255, 0, 255, 0.6);
transform: translate(2px, -2px);
}
20% {
box-shadow:
-1px 1px 0 rgba(0, 255, 255, 0.4),
1px -1px 0 rgba(255, 0, 255, 0.4);
transform: translate(1px, 1px);
}
25% {
box-shadow:
-4px 4px 0 rgba(0, 255, 255, 1),
4px -4px 0 rgba(255, 0, 255, 1);
transform: translate(-2px, 2px);
}
30% {
box-shadow:
3px -3px 0 rgba(0, 255, 255, 0.5),
-3px 3px 0 rgba(255, 0, 255, 0.5);
transform: translate(3px, -3px);
}
40% {
box-shadow:
-2px 2px 0 rgba(0, 255, 255, 0.9),
2px -2px 0 rgba(255, 0, 255, 0.9);
transform: translate(-1px, 1px);
}
50% {
box-shadow:
-1px -2px 0 rgba(0, 255, 255, 0.7),
2px -1px 0 rgba(255, 0, 255, 0.7);
transform: translate(1px, -1px);
}
60% {
box-shadow:
2px -2px 0 rgba(0, 255, 255, 0.3),
-2px 2px 0 rgba(255, 0, 255, 0.3);
transform: translate(2px, -2px);
}
75% {
box-shadow:
-3px 3px 0 rgba(0, 255, 255, 1),
3px -3px 0 rgba(255, 0, 255, 1);
transform: translate(-3px, 3px);
}
85% {
box-shadow:
-2px -2px 0 rgba(0, 255, 255, 0.2),
2px 2px 0 rgba(255, 0, 255, 0.2);
transform: translate(-2px, -2px);
}
100% {
box-shadow:
0px 0px 0 rgba(0, 255, 255, 0),
0px 0px 0 rgba(255, 0, 255, 0);
transform: translate(0, 0);
}
}
.visited\:text-blue-950:visited {
color: rgb(11 39 70 );
}
.visited\:text-purple-700:visited {
color: rgb(68 30 115 );
}
@ -1849,14 +1947,14 @@ article a:visited {
margin-bottom: 1rem;
}
.sm\:ml-0 {
margin-left: 0px;
}
.sm\:mb-0 {
margin-bottom: 0px;
}
.sm\:ml-0 {
margin-left: 0px;
}
.sm\:mt-0 {
margin-top: 0px;
}
@ -1893,10 +1991,6 @@ article a:visited {
margin-bottom: 2rem;
}
.md\:grid {
display: grid;
}
.md\:h-16 {
height: 4rem;
}
@ -2046,6 +2140,10 @@ article a:visited {
grid-row-start: auto;
}
.xl\:block {
display: block;
}
.xl\:grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}

View File

@ -42,15 +42,18 @@
</section> <!-- /#socials -->
<section id="showcase" class="hidden lg:block">
<h2 class="text-blue-950 font-bold text-2xl m-5 md:text-4xl">Showcase</h2>
<h2 class="text-blue-950 font-bold text-2xl m-5 md:text-4xl"><a href="/showcase" class="text-blue-950 no-underline">Showcase</a></h2>
<ul class="">
<ul class="mx-6">
{% for project in featured_projects %}
<li class="my-2">
<li class="my-4">
{% include "components/project_preview_card.html" %}
</li>
{% endfor %}
</ul>
<section class="text-center my-3 md:text-lg">
<a href="/showcase">check out more projects</a>
</section>
</section> <!-- /#showcase -->
</section> <!-- /#blog-container -->
{% endblock %}

View File

@ -15,7 +15,7 @@
</h3>
</header>
<section class="text-base leading-5 text-slate-800 md:text-xl text-justify">{{post.body|description_filter|safe}}</section>
<footer class="text-sm md:text-base lg:text-lg mt-3 sm:mt-0">
<footer class="text-sm md:text-base lg:text-lg mt-3 sm:mt-0 clear-both sm:clear-none">
<ul class="inline-block">
{% for tag in post.metadata.tags %}
<li class="inline-block">

View File

@ -1,4 +1,4 @@
<section class="border rounded-md bg-white m-4 p-4 break-inside-avoid">
<section class="border rounded-md bg-white p-4 break-inside-avoid">
<header class="px-4 mb-3">
<h2 class="text-xl font-semibold text-blue-900 md:text-2xl">
{% match project.metadata.link %}
@ -11,7 +11,7 @@
{% endmatch %}
</h2>
<section class="description text-slate-800 my-2 md:text-xl text-justify">
{{project.metadata.description|safe}}
{{project.body|safe}}
</section>
</header>
<!-- <hr class="border-blue-950 my-5"> -->

View File

@ -1,15 +1,14 @@
{% macro social_card_start(svg, heading, img) %}
{% macro social_card_start(svg, url, heading, img, class) %}
<section class="border rounded-md bg-pink-200 m-4 p-4 w-fit">
<a href="{{url}}" class="block no-underline border rounded-md bg-pink-200 m-4 p-4 max-w-[392px] {{class}}">
<header class="flex text-center justify-center items-center gap-2 mb-2">
<svg aria-hidden="true" class="h-7 w-7 fill-blue-950">
<use xlink:href="/svg/icons-sprite.svg#{{svg}}" />
</svg>
<h3 class="text-lg font-medium mb-1">{{heading|safe}}</h3>
<h3 class="text-lg font-medium mb-1 text-blue-950 visited:text-blue-950">{{heading|safe}}</h3>
</header>
{% let alt_text = format!("{svg} thumbnail") %}
{{ crate::picture_generator::picture_markup_generator::generate_picture_markup(img, 420, 200, alt_text, Some("h-auto"), true).unwrap_or("thumbnail not found".to_string())|safe }}
</section>
{{ crate::picture_generator::picture_markup_generator::generate_picture_markup(img, 360, 128, alt_text, Some("h-auto mx-auto rounded-sm"), true).unwrap_or("thumbnail not found".to_string())|safe }}
</a>
{% endmacro %}

View File

@ -1,6 +1,6 @@
{% macro talent_card(svg, heading, description) %}
<section class="flex border rounded bg-white m-4 p-3 max-w-xl">
<section class="flex border rounded bg-white m-4 p-3 max-w-[32rem]">
<aside class="flex justify-center items-center pr-3">
<svg aria-hidden="true" class="fill-blue-950 h-12 w-12 md:h-16 md:w-16">
<use xlink:href="/svg/icons-sprite.svg#{{svg}}" />

View File

@ -61,6 +61,9 @@
</li>
{% endfor %}
</ul>
<section class="text-center my-3 md:text-lg">
<a href="/blog">see all blog posts</a>
</section>
</section>
<section id="socials">

View File

@ -0,0 +1,24 @@
{% extends "base.html" %}
{% block content %}
<section id="project-list-container" class="max-w-maxindex mx-auto">
<section id="project-list">
{% if project_list.len() == 0 %}
<p class="no-posts">You've found void in the space.</p>
{% else %}
<h1 class="m-5 text-4xl text-blue-950 font-extrabold md:text-6xl">
Showcase
</h1>
<ul class="m-6 grid grid-flow-row gap-6 md:grid-cols-2 md:grid-rows-[masonry] md:justify-stretch md:items-stretch xl:grid-cols-3">
{% for project in project_list %}
<li>
{% include "components/project_preview_card.html" %}
</li>
{% endfor %}
</ul>
{% endif %}
</section> <!-- /#project-list -->
</section> <!-- /#project-list-container -->
{% endblock %}

View File

@ -1,9 +1,13 @@
<h2 class="text-blue-950 font-bold text-2xl m-5 md:text-4xl">Showcase</h2>
<h2 class="text-blue-950 font-bold text-2xl m-5 md:text-4xl"><a href="/showcase" class="text-blue-950 no-underline">Showcase</a></h2>
<ul class="mx-5 md:grid md:grid-cols-2 md:grid-rows-[masonry] md:justify-stretch md:items-stretch xl:grid-cols-3">
<ul class="mx-6 grid grid-flow-row gap-6 md:grid-cols-2 md:grid-rows-[masonry] md:justify-stretch md:items-stretch xl:grid-cols-3">
{% for project in featured_projects %}
<li class="my-2">
<li>
{% include "components/project_preview_card.html" %}
</li>
{% endfor %}
</ul>
<section class="text-center my-3 md:text-lg">
<a href="/showcase">check out more projects</a>
</section>

View File

@ -1,12 +1,14 @@
<h2 class="text-blue-950 font-bold text-2xl m-5 md:text-4xl">Socials</h2>
<section class="flex flex-col items-center">
{% call sc::social_card_start("twitch", "I stream (almost) regularly on <em>twitch.tv</em>", "images/social/instagram_plain.png") %}
<section class="grid grid-flow-row justify-center">
{% call sc::social_card_start("twitch", "https://twitch.tv/michalvankodev", "I stream (almost) regularly on <em>twitch.tv</em>", "images/social/twitch_wo.png", "social-card-twitch") %}
{% call sc::social_card_start("tiktok", "Highlights can be found on <em>TikTok</em>", "images/social/instagram_plain.png") %}
{% call sc::social_card_start("tiktok", "https://www.tiktok.com/@michalvankodev","Highlights can be found on <em>TikTok</em>", "images/social/tiktok_wo.png", "social-card-tiktok") %}
{% call sc::social_card_start("youtube", "Vlogs and highlights can be found on <em>YouTube</em>", "images/social/instagram_plain.png") %}
{% call sc::social_card_start("youtube", "https://www.youtube.com/@michalvankodev", "Videos and vlogs posted on <em>YouTube</em>", "images/social/youtube_wo.png", "social-card-youtube") %}
{% call sc::social_card_start("instagram", "Photos and stories shared on <em>Instagram</em>", "images/social/instagram_plain.png") %}
<div class="lg:hidden xl:block">
{% call sc::social_card_start("instagram", "https://www.instagram.com/michalvankodev/", "Photos and stories shared on <em>Instagram</em>", "images/social/instagram_plain.png", "social-card-instagram") %}
</div>
</section>

View File

@ -1,4 +1,5 @@
<footer>
<footer class="my-4">
<hr class="mb-4 border-slate-300 mx-5">
<p
class="text-center"
xmlns:cc="http://creativecommons.org/ns#"
@ -26,16 +27,24 @@
>CC BY-NC-ND 4.0<img
src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1"
alt="cc"
class="inline-block h-6 mx-0.5" /><img
class="inline-block h-6 mx-0.5"
height="24"
width="24" /><img
src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1"
alt="by"
class="inline-block h-6 mx-0.5" /><img
class="inline-block h-6 mx-0.5"
height="24"
width="24" /><img
src="https://mirrors.creativecommons.org/presskit/icons/nc.svg?ref=chooser-v1"
alt="nc"
class="inline-block h-6 mx-0.5" /><img
class="inline-block h-6 mx-0.5"
height="24"
width="24" /><img
src="https://mirrors.creativecommons.org/presskit/icons/nd.svg?ref=chooser-v1"
alt="nd"
class="inline-block h-6 mx-0.5"
height="24"
width="24"
/></a>
<!-- TODO Display link to feed with icon -->
<a href="/feed.xml" class="hidden">RSS feed</a>

View File

@ -1,20 +1,20 @@
<header class="min-h-full bg-blue-50 mb-5">
<nav class="flex">
{% match header_props.back_link %}
{% when Some with (link) %}
<a
class="px-3 py-2 text-lg font-medium print:hidden"
href="{{link.href}}"
>
{{link.label}}
</a>
{% when None %}
{% endmatch %}
<aside class="flex logo-section flex-grow justify-end content-end">
<a class="logo p-3 text-base" href="/">
@michalvankodev
</a>
</aside>
</nav>
<hr class="border-slate-300 mx-5">
<nav class="flex">
{% match header_props.back_link %}
{% when Some with (link) %}
<a
class="px-3 py-2 text-lg font-medium print:hidden"
href="{{link.href}}"
>
{{link.label}}
</a>
{% when None %}
{% endmatch %}
<aside class="flex logo-section flex-grow justify-end content-end">
<a class="logo p-3 text-base" href="/">
@michalvankodev
</a>
</aside>
</nav>
<hr class="border-slate-300 mx-5">
</header>

BIN
static/images/social/tiktok_wo.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
static/images/social/twitch_wo.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
static/images/social/youtube_wo.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

View File

@ -96,7 +96,7 @@ collections:
widget: boolean,
default: true,
}
- { label: Description, name: description, widget: markdown }
- { label: Description, name: body, widget: markdown }
- {
label: Cover image,
name: cover_image,
@ -141,7 +141,7 @@ collections:
widget: boolean,
default: true,
}
- { label: Description, name: description, widget: markdown }
- { label: Description, name: body, widget: markdown }
- {
label: Link,
name: link,