Compare commits

..

No commits in common. "85c98fac567947fe4d1034fe3e203f5a477e7dbe" and "6650366e600eb71e52e16b06731c0c0f431057d8" have entirely different histories.

61 changed files with 345 additions and 787 deletions

View File

@ -17,9 +17,7 @@ tags:
Creating my own website with blog was something I had in my mind from start of my professional career after I left school. Creating my own website with blog was something I had in my mind from start of my professional career after I left school.
I had a lot of new experience with development which I wanted to elaborate on and save into a small library so I can take a look back on my thoughts how they evolve over time. I had a lot of new experience with development which I wanted to elaborate on and save into a small library so I can take a look back on my thoughts how they evolve over time.
This was like 6 years ago. I had a successful first attempt at doing so. This was like 6 years ago. I had a successful first attempt at doing so. I created a WordPress with the simplest theme I've found and wrote some articles. I've published it under a domain of one of the first startups I've been part of. I still have a backup of the _Wordpress_ database somewhere so I can export those articles here when I will feel like doing so. The blog haven't lived for long as the domain once expired and I was not satisfied with it enough to deploy it somewhere else.
I created a WordPress with the simplest theme I've found and wrote some articles. I've published it under a domain of one of the first startups I've been part of. I still have a backup of the _Wordpress_ database somewhere so I can export those articles here when I will feel like doing so. The blog haven't lived for long as the domain once expired and I was not satisfied with it enough to deploy it somewhere else.
For all those years I was trying to create it in my spare time (of which wasn't that much apparently). There were several attempts. One with _Angular_ when it was "the cool kid on the block". Another one with _cycle.js_ which was not that far from being done. I regret it now as it would be really satisfying to finish that one. I had created neat <abbr title="Server side rendering">SSR</abbr> layer which was not really difficult to accomplish with _cycle.js_ as it is reactive and it only required skipping first client render of _virtual-dom_ after page load. I'm still in love with _cycle.js_ but after _sapper_ was released I've found out of its ability to create a nice **static site** I wanted to try it out. I think that the approach of **compiling the source code** as classic client applications have been doing for many years makes a lot of sense on the internet as well. This is the one thing I'd really like to be able to accomplish with reactive frameworks like _cycle.js_. For all those years I was trying to create it in my spare time (of which wasn't that much apparently). There were several attempts. One with _Angular_ when it was "the cool kid on the block". Another one with _cycle.js_ which was not that far from being done. I regret it now as it would be really satisfying to finish that one. I had created neat <abbr title="Server side rendering">SSR</abbr> layer which was not really difficult to accomplish with _cycle.js_ as it is reactive and it only required skipping first client render of _virtual-dom_ after page load. I'm still in love with _cycle.js_ but after _sapper_ was released I've found out of its ability to create a nice **static site** I wanted to try it out. I think that the approach of **compiling the source code** as classic client applications have been doing for many years makes a lot of sense on the internet as well. This is the one thing I'd really like to be able to accomplish with reactive frameworks like _cycle.js_.
@ -117,7 +115,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. 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. I am very satisfied with it and I recommend it.
## What's next {#whats-next} ## What's 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 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. 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") ![Egg fetcher game preview](/images/uploads/screenshot-from-2022-06-26-22-37-16.png "Egg fetcher game preview")
[You can check the result built with WASM here.](/showcase/egg-fetcher/) [You can check the rusult built with WASM here.](/showcase/egg-fetcher/)
## What's up with the weeklys ## What's up with the weeklys

View File

@ -1,6 +1,9 @@
--- ---
title: Panoramic title: Panoramic
displayed: true 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 classification: webapp
tags: tags:
- Webapp - Webapp
@ -11,6 +14,3 @@ tags:
- Data analytics - Data analytics
featured: false 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,6 +1,11 @@
--- ---
title: The Expert title: The Expert
displayed: true 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/ link: https://www.theexpert.com/
cover_image: /images/uploads/the-expert-logo.svg cover_image: /images/uploads/the-expert-logo.svg
classification: webapp classification: webapp
@ -12,8 +17,3 @@ tags:
- GraphQL - GraphQL
featured: true 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,13 +1,12 @@
--- ---
title: beinSports title: beinSports
displayed: true displayed: true
link: https://www.beinsports.com/en-us 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 cover_image: /images/uploads/bein_logo.af017869.webp
classification: website classification: website
tags: tags:
- Freemarker - Freemarker
featured: false 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

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

View File

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

View File

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

View File

@ -1,7 +1,9 @@
--- ---
title: FX Sales title: FX Sales
displayed: true displayed: true
link: https://www.caplin.com/business/fx-sales 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 cover_image: /images/uploads/fx_sales_screen2x.png
classification: webapp classification: webapp
tags: tags:
@ -10,6 +12,3 @@ tags:
- Knockout - Knockout
featured: false 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,7 +1,8 @@
--- ---
title: HeyLady! title: HeyLady!
displayed: true displayed: true
link: https://www.heylady.io/ description: A thriving online community supporting women to learn and practise
speaking English.
cover_image: /images/uploads/heyladylogo.svg cover_image: /images/uploads/heyladylogo.svg
classification: webapp classification: webapp
tags: tags:
@ -13,5 +14,3 @@ tags:
- PostgreSQL - PostgreSQL
featured: false featured: false
--- ---
A thriving online community supporting women to learn and practise
speaking English.

View File

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

View File

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

View File

@ -1,6 +1,10 @@
--- ---
title: Manualogic title: Manualogic
displayed: false 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 classification: webapp
tags: tags:
- Webapp - Webapp
@ -8,7 +12,3 @@ tags:
- RxJS - RxJS
featured: false 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

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

View File

@ -1,7 +1,9 @@
--- ---
title: responzIO title: responzIO
displayed: true displayed: true
link: https://www.croptech.com/ 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 cover_image: /images/uploads/responzio.png
classification: embedded classification: embedded
tags: tags:
@ -10,6 +12,3 @@ tags:
- NodeJS - NodeJS
featured: false 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

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

View File

@ -1,13 +1,13 @@
--- ---
title: Signal Hub Manager title: Signal Hub Manager
displayed: true 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 classification: webapp
tags: tags:
- Webapp - Webapp
- React - React
featured: false 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,10 +1,10 @@
--- ---
title: Skosy title: Skosy
displayed: false displayed: false
description: "*Skosy* is a web application whose purpose is to **automate the
writing of integration tests** for websites."
classification: webapp classification: webapp
tags: tags:
- Webapp - Webapp
featured: false featured: false
--- ---
*Skosy* is a web application whose purpose is to **automate the
writing of integration tests** for websites.

View File

@ -1,7 +1,9 @@
--- ---
title: Spreading the Web title: Spreading the Web
displayed: true displayed: true
link: https://michalvankodev.github.io/spreading-the-web 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 cover_image: /images/uploads/screenshot-from-2024-08-06-18-48-02.png
classification: presentation classification: presentation
tags: tags:
@ -9,6 +11,3 @@ tags:
- NodeJS - NodeJS
featured: false 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

@ -1,6 +1,9 @@
--- ---
title: The Grand Escape title: The Grand Escape
displayed: true 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 link: https://michalvankodev.itch.io/the-grand-escape
cover_image: /images/uploads/logo.png cover_image: /images/uploads/logo.png
classification: videogame classification: videogame
@ -9,6 +12,3 @@ tags:
- Bevy - Bevy
featured: true 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,13 +1,12 @@
--- ---
title: Unstoppable growth of front-end frameworks title: Unstoppable growth of front-end frameworks
displayed: true displayed: true
link: https://michalvankodev.github.io/unstoppable-growth-of-frontend-frameworks/ 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 classification: presentation
tags: tags:
- Presentation - Presentation
- NodeJS - NodeJS
featured: false 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

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

View File

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

View File

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

View File

@ -35,7 +35,6 @@ async fn main() {
.nest_service("/fonts", ServeDir::new("../static/fonts")) .nest_service("/fonts", ServeDir::new("../static/fonts"))
.nest_service("/generated_images", ServeDir::new("generated_images")) .nest_service("/generated_images", ServeDir::new("generated_images"))
.nest_service("/svg", ServeDir::new("../static/svg")) .nest_service("/svg", ServeDir::new("../static/svg"))
// TODO manifest logos have bad link, #directory-swap
.nest_service( .nest_service(
"/config.yml", "/config.yml",
ServeDir::new("../static/resources/config.yml"), ServeDir::new("../static/resources/config.yml"),
@ -54,11 +53,12 @@ async fn main() {
// TODO Socials // TODO Socials
// - fotos // - fotos
// background gradient color // TODO Colors
// TODO Change DNS system // Text slate, and gray should be somehow customised
// THINK deploy to alula? rather then katelyn? can be change whenever // STRONG bold-medium
// TODO print css and other 404 css linked in base.html
// TODO go live pipeline
// TODO after release // TODO after release
// OG tags // - contact
// Remove old web completely
// Restructure repository
// - projects page // - projects page
// - linktree page

View File

@ -64,7 +64,7 @@ pub async fn render_blog_post_list(
}; };
Ok(PostListTemplate { Ok(PostListTemplate {
title: "Blog posts".to_owned(), title: "Posts".to_owned(),
posts, posts,
tag, tag,
header_props, header_props,

View File

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

View File

@ -1,29 +0,0 @@
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.retain(|project| project.metadata.displayed);
project_list.reverse();
Ok(ProjectListTemplate {
title: "Showcase".to_owned(),
header_props: HeaderProps::default(),
project_list,
})
}

View File

@ -19,34 +19,16 @@ pub fn generate_picture_markup(
width: u32, width: u32,
height: u32, height: u32,
alt_text: &str, alt_text: &str,
class_name: Option<&str>,
generate_image: bool, generate_image: bool,
) -> Result<String, anyhow::Error> { ) -> Result<String, anyhow::Error> {
let exported_formats = get_export_formats(orig_img_path); let exported_formats = get_export_formats(orig_img_path);
let class_attr = if let Some(class) = class_name {
format!(r#"class="{class}""#)
} else {
"".to_string()
};
if exported_formats.is_empty() {
return Ok(formatdoc!(
r#"<img
src="{orig_img_path}"
width="{width}"
height="{height}"
{class_attr}
alt="{alt_text}"
>"#
));
}
let path_to_generated = get_generated_file_name(orig_img_path); let path_to_generated = get_generated_file_name(orig_img_path);
// TODO This should get removed when we move the project structure #directory-swap // TODO This should get removed when we move the project structure #move
let disk_img_path = let dev_only_img_path =
Path::new("../static/").join(orig_img_path.strip_prefix("/").unwrap_or(orig_img_path)); Path::new("../static/").join(orig_img_path.strip_prefix("/").unwrap_or(orig_img_path));
let orig_img_dimensions = image_dimensions(&disk_img_path)?; let orig_img_dimensions = image_dimensions(&dev_only_img_path)?;
let resolutions = get_resolutions(orig_img_dimensions, width, height); let resolutions = get_resolutions(orig_img_dimensions, width, height);
let path_to_generated_arc = Arc::new(path_to_generated); let path_to_generated_arc = Arc::new(path_to_generated);
@ -58,8 +40,8 @@ pub fn generate_picture_markup(
if generate_image { if generate_image {
rayon::spawn(move || { rayon::spawn(move || {
let orig_img = ImageReader::open(&disk_img_path) let orig_img = ImageReader::open(&dev_only_img_path)
.with_context(|| format!("Failed to read instrs from {:?}", &disk_img_path)) .with_context(|| format!("Failed to read instrs from {:?}", &dev_only_img_path))
.unwrap() .unwrap()
.decode() .decode()
.unwrap(); .unwrap();
@ -106,7 +88,6 @@ pub fn generate_picture_markup(
width="{width}" width="{width}"
height="{height}" height="{height}"
alt="{alt_text}" alt="{alt_text}"
{class_attr}
>"# >"#
); );
@ -315,14 +296,7 @@ fn test_generate_picture_markup() {
</picture>"#, </picture>"#,
}; };
assert_eq!( assert_eq!(
generate_picture_markup( generate_picture_markup(orig_img_path, width, height, "Testing image alt", false)
orig_img_path,
width,
height,
"Testing image alt",
None,
false
)
.expect("picture markup has to be generated"), .expect("picture markup has to be generated"),
result result
); );

View File

@ -1,4 +1,3 @@
use core::panic;
use std::path::Path; use std::path::Path;
use axum::http::StatusCode; use axum::http::StatusCode;
@ -10,7 +9,7 @@ use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd};
use serde::{de::DeserializeOwned, Deserialize, Deserializer}; use serde::{de::DeserializeOwned, Deserialize, Deserializer};
use syntect::{highlighting::ThemeSet, html::highlighted_html_for_string, parsing::SyntaxSet}; use syntect::{highlighting::ThemeSet, html::highlighted_html_for_string, parsing::SyntaxSet};
use tokio::fs; use tokio::fs;
use tracing::{debug, error, info}; use tracing::debug;
use crate::picture_generator::{ use crate::picture_generator::{
picture_markup_generator::generate_picture_markup, resolutions::get_max_resolution, picture_markup_generator::generate_picture_markup, resolutions::get_max_resolution,
@ -90,7 +89,6 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
let syntax_set = SyntaxSet::load_defaults_newlines(); let syntax_set = SyntaxSet::load_defaults_newlines();
let theme_set = ThemeSet::load_defaults(); let theme_set = ThemeSet::load_defaults();
let theme = theme_set.themes.get("InspiredGitHub").unwrap(); 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 { let parser = Parser::new_ext(markdown, options).map(|event| match event {
/* /*
@ -127,14 +125,8 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
); );
// Place image into the content with scaled reso to a boundary // Place image into the content with scaled reso to a boundary
let picture_markup = generate_picture_markup( let picture_markup =
&dest_url, generate_picture_markup(&dest_url, max_width, max_height, &title, generate_images)
max_width,
max_height,
&title,
None,
generate_images,
)
.unwrap_or(formatdoc!( .unwrap_or(formatdoc!(
r#" r#"
<img <img
@ -183,25 +175,18 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
text.to_lowercase() text.to_lowercase()
.replace(|c: char| !c.is_alphanumeric(), "-") .replace(|c: char| !c.is_alphanumeric(), "-")
}); });
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( Event::Html(
formatdoc!( formatdoc!(
r##"id="{heading_id}"> r##"
{text}"## <a name="{heading_id}" class="anchor" href="#{heading_id}">
<span class="header-link"></span>
</a>
{text}
"##
) )
.into(), .into(),
) )
} }
}
}
_ => Event::Text(text), _ => Event::Text(text),
}, },
Event::Start(Tag::Heading { Event::Start(Tag::Heading {
@ -211,10 +196,8 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
attrs: _, attrs: _,
}) => { }) => {
let id_str = id.map(|id| id.to_string()); let id_str = id.map(|id| id.to_string());
debug!("heading_start: {:?}, level: {}", &id_str, level);
text_kind = TextKind::Heading(id_str); text_kind = TextKind::Heading(id_str);
heading_ended = Some(false); Event::Html(format!("<{level}>").into())
Event::Html(format!("<{level} ").into())
} }
Event::Start(_) => event, Event::Start(_) => event,
Event::End(TagEnd::Image) => Event::Html("</figcaption></figure>".into()), Event::End(TagEnd::Image) => Event::Html("</figcaption></figure>".into()),
@ -224,7 +207,6 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
} }
Event::End(TagEnd::Heading(heading_level)) => { Event::End(TagEnd::Heading(heading_level)) => {
text_kind = TextKind::Text; text_kind = TextKind::Text;
heading_ended = None;
Event::End(TagEnd::Heading(heading_level)) Event::End(TagEnd::Heading(heading_level))
} }
_ => event, _ => event,

View File

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

View File

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

View File

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

View File

@ -69,11 +69,11 @@
} }
a { a {
@apply text-pink-800 underline underline-offset-2 hover:transition hover:text-blue-500; @apply text-pink-800 underline underline-offset-2;
}
strong { &:hover {
@apply font-medium; @apply transition text-blue-500;
}
} }
.article-body { .article-body {
@ -95,11 +95,12 @@ strong {
p { p {
@apply px-4 my-2 text-slate-950 text-justify mx-auto max-w-read md:text-lg md:my-8 lg:text-readxl; @apply px-4 my-2 text-slate-950 text-justify mx-auto max-w-read md:text-lg md:my-8 lg:text-readxl;
} }
strong {
@apply font-medium;
}
pre { pre {
@apply p-4 my-1 overflow-auto text-sm mx-auto max-w-read; @apply p-4 my-1 overflow-auto text-sm mx-auto max-w-read;
} }
figure { figure {
@apply p-4; @apply p-4;
@ -112,7 +113,7 @@ strong {
} }
table { table {
@apply text-sm mx-auto my-4 max-w-image table-auto border-collapse border-spacing-12 border border-slate-200 rounded md:text-base lg:text-xl lg:my-8; @apply text-sm mx-auto my-4 max-w-image table-auto border-collapse border-spacing-12 border border-gray-200 rounded md:text-base lg:text-xl lg:my-8;
} }
thead { thead {
@ -150,18 +151,7 @@ strong {
ul, ul,
ol { ol {
@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; @apply px-4 my-2 text-slate-950 mx-auto max-w-read md:text-lg md:my-8 lg:text-readxl;
& p {
@apply px-2;
}
}
ul {
@apply list-disc;
}
ol {
@apply list-decimal;
} }
iframe { iframe {
@ -169,122 +159,6 @@ strong {
} }
} }
article a {
@apply visited:text-purple-700;
}
.video-embed { .video-embed {
@apply m-4 mx-auto max-w-image aspect-video; @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

@ -107,7 +107,7 @@
} }
/* /*
! tailwindcss v3.4.13 | MIT License | https://tailwindcss.com ! tailwindcss v3.4.12 | MIT License | https://tailwindcss.com
*/ */
/* /*
@ -654,12 +654,8 @@ video {
grid-row: span 3 / span 3; grid-row: span 3 / span 3;
} }
.float-start { .m-0 {
float: inline-start; margin: 0px;
}
.clear-both {
clear: both;
} }
.m-1 { .m-1 {
@ -674,10 +670,6 @@ video {
margin: 1.25rem; margin: 1.25rem;
} }
.m-6 {
margin: 1.5rem;
}
.mx-0\.5 { .mx-0\.5 {
margin-left: 0.125rem; margin-left: 0.125rem;
margin-right: 0.125rem; margin-right: 0.125rem;
@ -713,16 +705,6 @@ video {
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
.my-3 {
margin-top: 0.75rem;
margin-bottom: 0.75rem;
}
.my-4 {
margin-top: 1rem;
margin-bottom: 1rem;
}
.my-5 { .my-5 {
margin-top: 1.25rem; margin-top: 1.25rem;
margin-bottom: 1.25rem; margin-bottom: 1.25rem;
@ -745,10 +727,6 @@ video {
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
} }
.mb-4 {
margin-bottom: 1rem;
}
.mb-5 { .mb-5 {
margin-bottom: 1.25rem; margin-bottom: 1.25rem;
} }
@ -757,10 +735,6 @@ video {
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
} }
.mr-3 {
margin-right: 0.75rem;
}
.mt-3 { .mt-3 {
margin-top: 0.75rem; margin-top: 0.75rem;
} }
@ -789,6 +763,10 @@ video {
display: none; display: none;
} }
.aspect-video {
aspect-ratio: 16 / 9;
}
.h-12 { .h-12 {
height: 3rem; height: 3rem;
} }
@ -797,6 +775,10 @@ video {
height: 1.5rem; height: 1.5rem;
} }
.h-64 {
height: 16rem;
}
.h-7 { .h-7 {
height: 1.75rem; height: 1.75rem;
} }
@ -805,14 +787,6 @@ video {
height: 240px; height: 240px;
} }
.h-auto {
height: auto;
}
.max-h-\[236px\] {
max-height: 236px;
}
.min-h-full { .min-h-full {
min-height: 100%; min-height: 100%;
} }
@ -833,12 +807,8 @@ video {
width: 180px; width: 180px;
} }
.max-w-\[32rem\] { .w-full {
max-width: 32rem; width: 100%;
}
.max-w-\[392px\] {
max-width: 392px;
} }
.max-w-maxindex { .max-w-maxindex {
@ -858,23 +828,22 @@ video {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
} }
.break-inside-avoid { .grid-flow-col {
-moz-column-break-inside: avoid; grid-auto-flow: column;
break-inside: avoid;
} }
.grid-flow-row { .grid-cols-\[max-content_1fr\] {
grid-auto-flow: row; grid-template-columns: max-content 1fr;
}
.grid-rows-\[max-content_1fr_max-content\] {
grid-template-rows: max-content 1fr max-content;
} }
.flex-row { .flex-row {
flex-direction: row; flex-direction: row;
} }
.flex-col {
flex-direction: column;
}
.place-content-center { .place-content-center {
place-content: center; place-content: center;
} }
@ -903,22 +872,26 @@ video {
gap: 0.5rem; gap: 0.5rem;
} }
.gap-6 { .gap-4 {
gap: 1.5rem; gap: 1rem;
}
.self-start {
align-self: flex-start;
} }
.self-center { .self-center {
align-self: center; align-self: center;
} }
.overflow-hidden {
overflow: hidden;
}
.rounded { .rounded {
border-radius: 0.25rem; border-radius: 0.25rem;
} }
.rounded-2xl {
border-radius: 1rem;
}
.rounded-full { .rounded-full {
border-radius: 9999px; border-radius: 9999px;
} }
@ -927,10 +900,6 @@ video {
border-radius: 0.375rem; border-radius: 0.375rem;
} }
.rounded-sm {
border-radius: 0.125rem;
}
.border { .border {
border-width: 1px; border-width: 1px;
} }
@ -939,6 +908,10 @@ video {
border-width: 2px; border-width: 2px;
} }
.border-4 {
border-width: 4px;
}
.border-blue-500 { .border-blue-500 {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(23 137 224 / var(--tw-border-opacity)); border-color: rgb(23 137 224 / var(--tw-border-opacity));
@ -949,11 +922,6 @@ video {
border-color: rgb(11 39 70 / var(--tw-border-opacity)); border-color: rgb(11 39 70 / var(--tw-border-opacity));
} }
.border-slate-300 {
--tw-border-opacity: 1;
border-color: rgb(203 213 225 / var(--tw-border-opacity));
}
.bg-blue-100 { .bg-blue-100 {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(225 239 253 / var(--tw-bg-opacity)); background-color: rgb(225 239 253 / var(--tw-bg-opacity));
@ -974,14 +942,19 @@ video {
background-color: rgb(255 255 255 / var(--tw-bg-opacity)); background-color: rgb(255 255 255 / var(--tw-bg-opacity));
} }
.fill-blue-900 {
fill: #103e6a;
}
.fill-blue-950 { .fill-blue-950 {
fill: #0b2746; fill: #0b2746;
} }
.object-contain {
-o-object-fit: contain;
object-fit: contain;
}
.p-0 {
padding: 0px;
}
.p-0\.5 { .p-0\.5 {
padding: 0.125rem; padding: 0.125rem;
} }
@ -1009,6 +982,11 @@ video {
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
} }
.py-5 {
padding-top: 1.25rem;
padding-bottom: 1.25rem;
}
.pr-3 { .pr-3 {
padding-right: 0.75rem; padding-right: 0.75rem;
} }
@ -1113,16 +1091,21 @@ video {
color: rgb(11 39 70 / var(--tw-text-opacity)); color: rgb(11 39 70 / var(--tw-text-opacity));
} }
.text-gray-600 {
--tw-text-opacity: 1;
color: rgb(75 85 99 / var(--tw-text-opacity));
}
.text-gray-800 {
--tw-text-opacity: 1;
color: rgb(31 41 55 / var(--tw-text-opacity));
}
.text-pink-950 { .text-pink-950 {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(80 2 56 / var(--tw-text-opacity)); color: rgb(80 2 56 / var(--tw-text-opacity));
} }
.text-slate-600 {
--tw-text-opacity: 1;
color: rgb(71 85 105 / var(--tw-text-opacity));
}
.text-slate-800 { .text-slate-800 {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(30 41 59 / var(--tw-text-opacity)); color: rgb(30 41 59 / var(--tw-text-opacity));
@ -1132,6 +1115,11 @@ video {
text-decoration-line: none; text-decoration-line: none;
} }
.drop-shadow-md {
--tw-drop-shadow: drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06));
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
}
.transition-colors { .transition-colors {
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
@ -1143,20 +1131,17 @@ a {
color: rgb(146 22 110 / var(--tw-text-opacity)); color: rgb(146 22 110 / var(--tw-text-opacity));
text-decoration-line: underline; text-decoration-line: underline;
text-underline-offset: 2px; text-underline-offset: 2px;
} &:hover {
a:hover {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(23 137 224 / var(--tw-text-opacity)); color: rgb(23 137 224 / var(--tw-text-opacity));
}
&:hover {
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms; transition-duration: 150ms;
} }
strong {
font-weight: 500;
} }
.article-body { .article-body {
@ -1422,6 +1407,9 @@ strong {
font-weight: 400; font-weight: 400;
} }
} }
strong {
font-weight: 500;
}
pre { pre {
margin-top: 0.25rem; margin-top: 0.25rem;
margin-bottom: 0.25rem; margin-bottom: 0.25rem;
@ -1524,7 +1512,7 @@ strong {
} }
table { table {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(226 232 240 / var(--tw-border-opacity)); border-color: rgb(229 231 235 / var(--tw-border-opacity));
} }
table { table {
font-size: 0.875rem; font-size: 0.875rem;
@ -1726,11 +1714,8 @@ strong {
} }
ul, ul,
ol { ol {
padding-left: 2.5rem; padding-left: 1rem;
} padding-right: 1rem;
ul,
ol {
padding-right: 1.5rem;
} }
ul, ul,
ol { ol {
@ -1751,12 +1736,6 @@ strong {
line-height: 1.75rem; line-height: 1.75rem;
} }
} }
@media (min-width: 1024px) {
ul,
ol {
padding-left: 3.5rem;
}
}
@media (min-width: 1024px) { @media (min-width: 1024px) {
ul, ul,
ol { ol {
@ -1766,19 +1745,6 @@ strong {
font-weight: 400; 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 { iframe {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
@ -1798,10 +1764,6 @@ strong {
} }
} }
article a:visited {
color: rgb(68 30 115 );
}
.video-embed { .video-embed {
margin: 1rem; margin: 1rem;
margin-left: auto; margin-left: auto;
@ -1810,192 +1772,21 @@ article a:visited {
max-width: min(70rem, 95vw); 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 );
}
.hover\:bg-pink-200:hover { .hover\:bg-pink-200:hover {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(255 207 247 / var(--tw-bg-opacity)); background-color: rgb(255 207 247 / var(--tw-bg-opacity));
} }
.hover\:fill-blue-400:hover {
fill: #42a6f0;
}
@media (min-width: 640px) {
.sm\:float-none {
float: none;
}
.sm\:clear-none {
clear: none;
}
.sm\:my-4 {
margin-top: 1rem;
margin-bottom: 1rem;
}
.sm\:mb-0 {
margin-bottom: 0px;
}
.sm\:ml-0 {
margin-left: 0px;
}
.sm\:mt-0 {
margin-top: 0px;
}
.sm\:grid {
display: grid;
}
.sm\:grid-flow-col {
grid-auto-flow: column;
}
.sm\:grid-cols-\[max-content_1fr\] {
grid-template-columns: max-content 1fr;
}
.sm\:grid-rows-\[max-content_1fr_max-content\] {
grid-template-rows: max-content 1fr max-content;
}
.sm\:gap-4 {
gap: 1rem;
}
.sm\:py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
}
}
@media (min-width: 768px) { @media (min-width: 768px) {
.md\:my-8 { .md\:my-8 {
margin-top: 2rem; margin-top: 2rem;
margin-bottom: 2rem; margin-bottom: 2rem;
} }
.md\:grid {
display: grid;
}
.md\:h-16 { .md\:h-16 {
height: 4rem; height: 4rem;
} }
@ -2074,16 +1865,6 @@ article a:visited {
grid-row-start: 2; grid-row-start: 2;
} }
.lg\:mx-auto {
margin-left: auto;
margin-right: auto;
}
.lg\:my-6 {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
.lg\:mt-20 { .lg\:mt-20 {
margin-top: 5rem; margin-top: 5rem;
} }
@ -2145,10 +1926,6 @@ article a:visited {
grid-row-start: auto; grid-row-start: auto;
} }
.xl\:block {
display: block;
}
.xl\:grid-cols-3 { .xl\:grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
} }
@ -2157,9 +1934,3 @@ article a:visited {
grid-template-columns: 1fr 2fr; grid-template-columns: 1fr 2fr;
} }
} }
@media print {
.print\:hidden {
display: none;
}
}

View File

@ -1,7 +1,7 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<title>{% block title %} {{title}} {% endblock %} @michalvankodev</title> <title>{% block title %} {{title}} @michalvankodev {% endblock %}</title>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" /> <meta name="viewport" content="width=device-width,initial-scale=1.0" />
<meta name="theme-color" content="#333333" /> <meta name="theme-color" content="#333333" />
@ -13,16 +13,19 @@
rel="alternate" rel="alternate"
type="application/rss+xml" type="application/rss+xml"
title="RSS feed for latest posts" title="RSS feed for latest posts"
href="/feed.xml" href="https://michalvanko.dev/feed.xml"
/> />
<!-- Tailwind output file --> <!-- Tailwind output file -->
<link rel="stylesheet" href="/styles/output.css" /> <link rel="stylesheet" href="/styles/output.css" />
<link rel="stylesheet" href="/print.css" media="print" />
<link rel="stylesheet" href="/fonts.css" />
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/manifest.json" />
<link rel="stylesheet" href="/prism.css" />
<link rel="icon" type="image/svg+xml" href="/images/m-logo.svg" /> <link rel="icon" type="image/svg+xml" href="/m-logo.svg" />
<link rel="icon" type="image/png" href="/images/m-logo-192.png" /> <link rel="icon" type="image/png" href="/m-logo-192.png" />
</head> </head>
<body class="bg-blue-50"> <body class="bg-blue-50">
{% include "site_header.html" %} {% include "site_header.html" %}

View File

@ -8,7 +8,7 @@
<h1 class="text-3xl md:text-4xl lg:text-6xl lg:mt-20 text-blue-900 mb-3 font-bold">{{title}}</h1> <h1 class="text-3xl md:text-4xl lg:text-6xl lg:mt-20 text-blue-900 mb-3 font-bold">{{title}}</h1>
<aside class="flex justify-between flex-row"> <aside class="flex justify-between flex-row">
{% include "post_tag_list.html" %} {% include "post_tag_list.html" %}
<section class="created-at m-1 text-right text-sm text-slate-600 md:text-lg"> <section class="created-at m-1 text-right text-sm text-gray-600 md:text-lg">
<span>Published on</span> <span>Published on</span>
<time datetime="{date}"> {{date|pretty_date}} </time> <time datetime="{date}"> {{date|pretty_date}} </time>
</section> </section>
@ -21,7 +21,6 @@
</article> </article>
<!-- TODO: Next recommendations for reading --> <!-- TODO: Next recommendations for reading -->
<!-- TODO: Bact to all posts -->
{# footer #} {# footer #}
{% endblock %} {% endblock %}

View File

@ -9,7 +9,7 @@
{% else %} {% else %}
<h1 class="m-5 text-4xl text-blue-950 font-extrabold md:text-6xl"> <h1 class="m-5 text-4xl text-blue-950 font-extrabold md:text-6xl">
{% if let Some(t) = tag %} {% if let Some(t) = tag %}
#{{t}} <em>{{t}}</em>
{% else %} {% else %}
Blog posts Blog posts
{% endif %} {% endif %}
@ -25,13 +25,13 @@
</ul> </ul>
</section> </section>
<hr class="border-slate-300 m-5 md:my-8"> <hr class="border-blue-950 m-5 md:my-8">
<ul class="mx-5"> <ul class="mx-5">
{% for post in posts %} {% for post in posts %}
<li> <li>
{% include "components/blog_post_preview.html" %} {% include "components/blog_post_preview.html" %}
<hr class="border-slate-300 my-5 md:my-8"> <hr class="border-blue-950 my-5 md:my-8">
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
@ -42,18 +42,15 @@
</section> <!-- /#socials --> </section> <!-- /#socials -->
<section id="showcase" class="hidden lg:block"> <section id="showcase" class="hidden lg:block">
<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> <h2 class="text-blue-950 font-bold text-2xl m-5 md:text-4xl">Showcase</h2>
<ul class="mx-6"> <ul class="">
{% for project in featured_projects %} {% for project in featured_projects %}
<li class="my-4"> <li class="my-2">
{% include "components/project_preview_card.html" %} {% include "components/project_preview_card.html" %}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
<section class="text-center my-3 md:text-lg">
<a href="/showcase">check out more projects</a>
</section>
</section> <!-- /#showcase --> </section> <!-- /#showcase -->
</section> <!-- /#blog-container --> </section> <!-- /#blog-container -->
{% endblock %} {% endblock %}

View File

@ -1,3 +1,3 @@
<div class="w-[180px] h-[240px] bg-blue-100 flex justify-center items-center"> <div class="rounded-2xl w-[180px] h-[240px] bg-blue-100 border-4 border-blue-500 flex justify-center items-center">
<span class="text-blue-500 text-8xl -translate-y-1.5">{{post.metadata.title|fmt("{:.1}")|lower}}</span> <span class="text-blue-500 text-8xl -translate-y-1.5">{{post.metadata.title|fmt("{:.1}")|lower}}</span>
</div> </div>

View File

@ -1,8 +1,8 @@
<article class="sm:grid sm:grid-cols-[max-content_1fr] sm:grid-rows-[max-content_1fr_max-content] sm:grid-flow-col sm:gap-4 md:gap-x-8 break-inside-avoid clear-both sm:clear-none"> <article class="grid grid-cols-[max-content_1fr] grid-rows-[max-content_1fr_max-content] grid-flow-col gap-4 md:gap-x-8">
<aside class="row-span-3 self-center float-start sm:float-none mr-3 mb-3 sm:ml-0 sm:mb-0"> <aside class="row-span-3 self-center">
{% match post.metadata.thumbnail %} {% match post.metadata.thumbnail %}
{% when Some with (orig_path) %} {% when Some with (orig_path) %}
{{ crate::picture_generator::picture_markup_generator::generate_picture_markup(orig_path, 180, 240, "Article thumbnail", None, true).unwrap_or("thumbnail not found".to_string())|safe }} {{ crate::picture_generator::picture_markup_generator::generate_picture_markup(orig_path, 180, 240, "Article thumbnail", true).unwrap_or("thumbnail not found".to_string())|safe }}
{% when None %} {% when None %}
<div> <div>
{% include "components/blog_post_default_thumbnail.html" %} {% include "components/blog_post_default_thumbnail.html" %}
@ -11,11 +11,11 @@
</aside> </aside>
<header> <header>
<h3 class="text-lg font-bold mb-1 md:text-3xl"> <h3 class="text-lg font-bold mb-1 md:text-3xl">
<a rel="prefetch" href="/blog/{{post.slug}}" class="text-blue-950 visited:text-purple-700 no-underline">{{post.metadata.title}}</a> <a rel="prefetch" href="/blog/{{post.slug}}" class="text-blue-950 no-underline">{{post.metadata.title}}</a>
</h3> </h3>
</header> </header>
<section class="text-base leading-5 text-slate-800 md:text-xl text-justify">{{post.body|description_filter|safe}}</section> <section class="text-base leading-5 text-gray-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">
<ul class="inline-block"> <ul class="inline-block">
{% for tag in post.metadata.tags %} {% for tag in post.metadata.tags %}
<li class="inline-block"> <li class="inline-block">

View File

@ -1,4 +1,4 @@
<section class="border rounded-md bg-white p-4 break-inside-avoid"> <article class="border rounded-md bg-white m-4 p-4">
<header class="px-4 mb-3"> <header class="px-4 mb-3">
<h2 class="text-xl font-semibold text-blue-900 md:text-2xl"> <h2 class="text-xl font-semibold text-blue-900 md:text-2xl">
{% match project.metadata.link %} {% match project.metadata.link %}
@ -11,24 +11,23 @@
{% endmatch %} {% endmatch %}
</h2> </h2>
<section class="description text-slate-800 my-2 md:text-xl text-justify"> <section class="description text-slate-800 my-2 md:text-xl text-justify">
{{project.body|safe}} {{project.metadata.description|safe}}
</section> </section>
</header> </header>
<!-- <hr class="border-blue-950 my-5"> --> <!-- <hr class="border-blue-950 my-5"> -->
{% match project.metadata.cover_image %} {% match project.metadata.cover_image %}
{% when Some with (source) %} {% when Some with (source) %}
{% let picture = crate::picture_generator::picture_markup_generator::generate_picture_markup(source, 420, 236, "Project cover", Some("max-h-[236px]"), true).unwrap_or("cover not found".to_string()) %} <figure class="mx-4 my-2">
<figure class="mx-4 my-2 flex justify-center">
{% match project.metadata.link %} {% match project.metadata.link %}
{% when Some with (href) %} {% when Some with (href) %}
<a href="{{href}}"> <a href="{{href}}">
{{picture|safe}} <img src="{{source}}" class="object-contain w-full aspect-video"/>
</a> </a>
{% when None %} {% when None %}
{{picture|safe}} <img src="{{source}}" />
{% endmatch %} {% endmatch %}
<!-- TODO <figure> generate_image --> <!-- TODO <figure> -->
</figure> </figure>
{% when None %} {% when None %}
{% endmatch %} {% endmatch %}
@ -46,4 +45,4 @@
{% endfor %} {% endfor %}
</ul> </ul>
</footer> </footer>
</section> </article>

View File

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

View File

@ -1,6 +1,6 @@
{% macro talent_card(svg, heading, description) %} {% macro talent_card(svg, heading, description) %}
<section class="flex border rounded bg-white m-4 p-3 max-w-[32rem]"> <section class="flex border rounded bg-white m-4 p-3">
<aside class="flex justify-center items-center pr-3"> <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"> <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}}" /> <use xlink:href="/svg/icons-sprite.svg#{{svg}}" />
@ -10,7 +10,7 @@
<header> <header>
<h3 class="text-lg font-medium mb-1 md:text-2xl">{{heading}}</h3> <h3 class="text-lg font-medium mb-1 md:text-2xl">{{heading}}</h3>
</header> </header>
<p class="text-sm leading-5 text-slate-800 md:text-lg">{{description|safe}}</p> <p class="text-sm leading-5 text-gray-800 md:text-lg">{{description|safe}}</p>
</section> </section>
</section> </section>

View File

@ -1,20 +1,19 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<section id="contact-page"> <h1 class="mx-6 mt-3 text-4xl text-blue-950 font-extrabold">
<h1 class="mx-6 mt-3 text-4xl text-blue-950 font-extrabold lg:mx-auto max-w-read">
Contact Contact
</h1> </h1>
<ul class="mx-6"> <ul class="mx-6">
{% for link in links %} {% for link in links %}
<li class="my-2 sm:my-4 lg:my-6 max-w-[32rem] mx-auto"> <li class="my-6">
<a <a
class="flex border-2 place-content-center items-center rounded-full text-blue-900 border-blue-500 py-2 sm:py-4 hover:bg-pink-200 fill-blue-900 hover:fill-blue-400 transition-colors no-underline" class="flex border-2 place-content-center items-center rounded-full border-blue-500 py-5 hover:bg-pink-200 transition-colors"
href="{{link.href}}" href="{{link.href}}"
title="{{link.title}}" title="{{link.title}}"
> >
<svg aria-hidden="true" class="h-6 w-6 mx-2 self-start"> <svg aria-hidden="true" class="h-6 w-6 fill-blue-950 mx-2">
<use xlink:href="/svg/icons-sprite.svg#{{link.svg}}" /> <use xlink:href="/svg/icons-sprite.svg#{{link.svg}}" />
</svg> </svg>
<span class="text-lg font-semibold">{{link.label}}</span> <span class="text-lg font-semibold">{{link.label}}</span>
@ -22,6 +21,5 @@
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
</section>
{% endblock %} {% endblock %}

View File

@ -7,22 +7,23 @@
{% block content %} {% block content %}
<section class="index-container lg:grid lg:grid-cols-2 xl:grid-cols-[1fr_2fr] lg:gap-y-8 lg:gap-x-32 max-w-maxindex mx-auto"> <section class="index-container lg:grid lg:grid-cols-2 xl:grid-cols-[1fr_2fr] lg:gap-y-8 lg:gap-x-32 max-w-maxindex mx-auto">
<section id="about-me"> <section id="about-me">
<!-- <header class="index-header hidden"> --> <header class="index-header hidden">
<!-- <figure class="profile-pic"> --> <figure class="profile-pic">
<!-- <picture> --> <picture>
<!-- <img --> <img
<!-- alt="Portrait" --> alt="Portrait"
<!-- {# TODO Take a new photo #} --> {# TODO generate `srcset` for optimal image #}
<!-- src="/images/profile-portugal-landscape.jpg" --> {# TODO Take a new photo #}
<!-- /> --> src="/images/profile-portugal-landscape.jpg"
<!-- </picture> --> />
<!-- </figure> --> </picture>
</figure>
<!-- <p class="motto"> --> <p class="motto">
<!-- <cite>“Let your ambition carry you.”</cite> --> <cite>“Let your ambition carry you.”</cite>
<!-- <span class="cite-owner">- La Flame</span> --> <span class="cite-owner">- La Flame</span>
<!-- </p> --> </p>
<!-- </header> --> </header>
<h2 class="text-blue-950 font-bold text-2xl m-5 md:text-4xl">About me</h2> <h2 class="text-blue-950 font-bold text-2xl m-5 md:text-4xl">About me</h2>
@ -33,10 +34,10 @@
<em> <a href="https://en.wikipedia.org/wiki/Programmer">programmer</a> </em> <em> <a href="https://en.wikipedia.org/wiki/Programmer">programmer</a> </em>
. I am developing software for more than half of my life and <strong>I love it!</strong> Sometimes I stream working on my side projects and building a <a href="https://discord.gg/2cGg7kwZEh">community of like minded people</a>. Here you can find blogs of my thoughts and journeys, as well as links to my socials where you can see other content.</p> . I am developing software for more than half of my life and <strong>I love it!</strong> Sometimes I stream working on my side projects and building a <a href="https://discord.gg/2cGg7kwZEh">community of like minded people</a>. Here you can find blogs of my thoughts and journeys, as well as links to my socials where you can see other content.</p>
<section id="talent-cards" class="flex flex-col items-center"> <section id="talent-cards">
{% call tc::talent_card("code", "Web development", "Extensive expertise in creating performant, live web applications and websites") %} {% call tc::talent_card("code", "Web development", "Extensive expertise in creating performant, live web applications and websites") %}
{% call tc::talent_card("gamepad", "Game development", "Extensive expertise in creating performant, live web applications and websites") %} {% call tc::talent_card("gamepad", "Game development", "Extensive expertise in creating performant, live web applications and websites") %}
{% call tc::talent_card("person-chalkboard", "Mentoring & Consulting", "I offer consulting sessions to assist you in developing <strong>higher-quality software</strong> and share insights from crafting robust, professional web applications. <a href=\"https://calendly.com/michalvankosk/30min\">Schedule a session with me</a> and elevate your projects together.") %} {% call tc::talent_card("person-chalkboard", "Mentoring & Consulting", "I offer consulting sessions to assist you in developing <strong>higher-quality software</strong> and share insights from crafting robust, professional web applications. <a href=\"TODO callendly\">Schedule a session with me</a> and elevate your projects together.") %}
</section> </section>
</section> </section>
@ -51,26 +52,23 @@
{% endfor %} {% endfor %}
</ul> </ul>
</section> </section>
<hr class="border-slate-300 m-5"> <hr class="border-blue-950 m-5">
<ul class="mx-5"> <ul class="mx-5">
{% for post in featured_blog_posts %} {% for post in featured_blog_posts %}
<li> <li>
{% include "components/blog_post_preview.html" %} {% include "components/blog_post_preview.html" %}
<hr class="border-slate-300 my-5 md:my-8"> <hr class="border-blue-950 my-5 md:my-8">
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
<section class="text-center my-3 md:text-lg">
<a href="/blog">see all blog posts</a>
</section>
</section> </section>
<section id="socials"> <section id="socials">
{% include "sections/social.html" %} {% include "sections/social.html" %}
</section> </section>
<hr class="border-slate-300 m-5 lg:hidden"> <hr class="border-blue-950 m-5 lg:hidden">
<section id="showcase" class="col-span-2"> <section id="showcase" class="col-span-2">
{% include "sections/showcase.html" %} {% include "sections/showcase.html" %}

View File

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

View File

@ -1,14 +1,52 @@
<h2 class="text-blue-950 font-bold text-2xl m-5 md:text-4xl">Socials</h2> <h2 class="text-blue-950 font-bold text-2xl m-5 md:text-4xl">Socials</h2>
{% call sc::social_card_start("twitch", "I stream (almost) regularly on <em>twitch.tv</em>") %}
<!-- <script src= "https://player.twitch.tv/js/embed/v1.js"></script> -->
<!-- <div id="twitch-player" class="h-64 aspect-video rounded overflow-hidden"></div> -->
<!-- <script type="text/javascript"> -->
<!-- var options = { -->
<!-- width: "100%", -->
<!-- height: "100%", -->
<!-- channel: "michalvankodev", -->
<!-- parent: ["localhost"] -->
<!-- }; -->
<!-- var player = new Twitch.Player("twitch-player", options); -->
<!-- player.setVolume(0.5); -->
</script>
{% call sc::social_card_end() %}
<section class="grid grid-flow-row justify-center"> {% call sc::social_card_start("tiktok", "Highlights can be found on <em>TikTok</em>") %}
{% 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") %} <!-- STYLES needed to overwrite tiktok embed css -->
<!-- <blockquote -->
<!-- class="h-64 aspect-video overflow-hidden p-0 m-0 tiktok-embed bg-pink-200" -->
<!-- cite="https://www.tiktok.com/@michalvankodev" -->
<!-- data-unique-id="michalvankodev" -->
<!-- data-embed-from="embed_page" -->
<!-- data-embed-type="creator" -->
<!-- style="max-width:780px; min-width:288px; margin: 0; padding: 0; border-radius: 8px" -->
<!-- > -->
<!-- <section> -->
<!-- <a target="_blank" href="https://www.tiktok.com/@michalvankodev?refer=creator_embed">@michalvankodev</a> -->
<!-- </section> -->
<!-- </blockquote> -->
<!-- <script async src="https://www.tiktok.com/embed.js"></script> -->
{% call sc::social_card_end() %}
{% 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>") %}
<!-- TODO create our own youtube widget which will populate this window on build -->
<!-- <iframe -->
<!-- class="h-64 aspect-video" -->
<!-- id="ytplayer" -->
<!-- type="text/html" -->
<!-- width="100%" -->
<!-- height="100%" -->
<!-- src="https://www.youtube.com/embed/?listType=playlist&list=PLjUl8tFKyR8rCsckLn93PAwQg6tf0cyBl&enablejsapi=1&color=white" -->
<!-- frameborder="0" -->
<!-- allowfullscreen -->
<!-- ></iframe> -->
{% call sc::social_card_end() %}
{% 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") %}
<div class="lg:hidden xl:block"> {% call sc::social_card_start("instagram", "Photos and stories shared on <em>Instagram</em>") %}
{% 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") %} <!-- <blockquote class="instagram-media aspect-video h-64" data-instgrm-permalink="https://www.instagram.com/michalvankodev/" data-instgrm-version="12" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; height:256px; max-height:100%;"></blockquote><script async src="https://www.instagram.com/embed.js"></script> -->
</div> {% call sc::social_card_end() %}
</section>

View File

@ -1,5 +1,4 @@
<footer class="my-4"> <footer>
<hr class="mb-4 border-slate-300 mx-5">
<p <p
class="text-center" class="text-center"
xmlns:cc="http://creativecommons.org/ns#" xmlns:cc="http://creativecommons.org/ns#"
@ -15,7 +14,7 @@
<a <a
rel="cc:attributionURL dct:creator" rel="cc:attributionURL dct:creator"
property="cc:attributionName" property="cc:attributionName"
href="/contact" href="https://michalvanko.dev/"
>Michal Vanko</a >Michal Vanko</a
> >
is licensed under is licensed under

View File

@ -3,7 +3,7 @@
{% match header_props.back_link %} {% match header_props.back_link %}
{% when Some with (link) %} {% when Some with (link) %}
<a <a
class="px-3 py-2 text-lg font-medium print:hidden" class="px-3 py-2 text-lg font-medium drop-shadow-md"
href="{{link.href}}" href="{{link.href}}"
> >
{{link.label}} {{link.label}}
@ -16,5 +16,5 @@
</a> </a>
</aside> </aside>
</nav> </nav>
<hr class="border-slate-300 mx-5"> <hr class="border-blue-950 mx-5">
</header> </header>

BIN
static/images/social/instagram_plain.png (Stored with Git LFS)

Binary file not shown.

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

Binary file not shown.

Binary file not shown.

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

Binary file not shown.

Binary file not shown.

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

Binary file not shown.

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

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