Tailwind for post_list
This commit is contained in:
parent
0cb8e84666
commit
8201312c26
9
axum_server/.zellij/default.kdl
Normal file
9
axum_server/.zellij/default.kdl
Normal file
@ -0,0 +1,9 @@
|
||||
layout {
|
||||
pane size=1 borderless=true {
|
||||
plugin location="zellij:tab-bar"
|
||||
}
|
||||
pane
|
||||
pane size=2 borderless=true {
|
||||
plugin location="zellij:status-bar"
|
||||
}
|
||||
}
|
25
axum_server/.zellij/dev-layout.kdl
Normal file
25
axum_server/.zellij/dev-layout.kdl
Normal file
@ -0,0 +1,25 @@
|
||||
layout {
|
||||
pane size=1 borderless=true {
|
||||
plugin location="zellij:tab-bar"
|
||||
}
|
||||
pane split_direction="vertical" focus=true {
|
||||
pane edit="src/main.rs"
|
||||
pane split_direction="horizontal" size=60 {
|
||||
just { args "server_dev"; }
|
||||
just { args "test"; }
|
||||
}
|
||||
}
|
||||
pane_template name="just" {
|
||||
command "just"
|
||||
start_suspended true
|
||||
}
|
||||
floating_panes {
|
||||
pane {
|
||||
command "just"
|
||||
args "tailwind"
|
||||
}
|
||||
}
|
||||
pane size=2 borderless=true {
|
||||
plugin location="zellij:status-bar"
|
||||
}
|
||||
}
|
@ -17,5 +17,6 @@ serde = "1.0.195"
|
||||
serde_json = "1.0.111"
|
||||
tokio = { version = "1.35.1", features = ["full"] }
|
||||
tower-http = { version = "0.5.0", features = ["trace", "fs"] }
|
||||
tower-livereload = "0.9.2"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
|
@ -11,6 +11,10 @@ server_dev:
|
||||
dev:
|
||||
(just server_dev; just tailwind) | parallel
|
||||
|
||||
# Run dev server in watch mode
|
||||
test:
|
||||
cargo test
|
||||
|
||||
# Run server in production mode
|
||||
prod:
|
||||
cargo run --release
|
||||
|
@ -4,26 +4,20 @@ pub struct Link {
|
||||
}
|
||||
|
||||
pub struct HeaderProps {
|
||||
pub links: Vec<Link>,
|
||||
pub back_link: Option<Link>,
|
||||
}
|
||||
|
||||
impl Default for HeaderProps {
|
||||
fn default() -> Self {
|
||||
Self { back_link: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl HeaderProps {
|
||||
pub fn with_back_link(link: Link) -> Self {
|
||||
Self {
|
||||
links: vec![
|
||||
Link {
|
||||
href: "/".to_string(),
|
||||
label: "Introduction".to_string(),
|
||||
},
|
||||
Link {
|
||||
href: "/blog".to_string(),
|
||||
label: "Blog".to_string(),
|
||||
},
|
||||
Link {
|
||||
href: "/portfolio".to_string(),
|
||||
label: "Portfolio".to_string(),
|
||||
},
|
||||
],
|
||||
back_link: Some(link),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use axum;
|
||||
use tower_http::services::ServeDir;
|
||||
use tower_livereload::LiveReloadLayer;
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
mod components;
|
||||
@ -25,6 +26,10 @@ async fn main() {
|
||||
|
||||
// build our application with a single route
|
||||
let app = router::get_router().nest_service("/styles", ServeDir::new("styles"));
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
let app = app.layer(LiveReloadLayer::new());
|
||||
|
||||
// run our app with hyper, listening globally on port 3080
|
||||
let port = std::option_env!("PORT").unwrap_or("3080");
|
||||
let addr = format!("0.0.0.0:{}", port);
|
||||
|
@ -4,7 +4,7 @@ use axum::{extract::Path, http::StatusCode};
|
||||
use crate::{
|
||||
components::{
|
||||
site_footer::{render_site_footer, SiteFooter},
|
||||
site_header::HeaderProps,
|
||||
site_header::{HeaderProps, Link},
|
||||
},
|
||||
post_list::get_post_list,
|
||||
post_parser::ParseResult,
|
||||
@ -50,12 +50,21 @@ pub async fn render_post_list(tag: Option<Path<String>>) -> Result<PostListTempl
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
// TODO if we have a tag we want to go back to all posts, otherwise we don't
|
||||
let header_props = match tag {
|
||||
Some(_) => HeaderProps::with_back_link(Link {
|
||||
href: "/blog".to_string(),
|
||||
label: "All posts".to_string(),
|
||||
}),
|
||||
None => HeaderProps::default(),
|
||||
};
|
||||
|
||||
Ok(PostListTemplate {
|
||||
title: "Posts".to_owned(),
|
||||
posts,
|
||||
tag,
|
||||
site_footer,
|
||||
header_props: HeaderProps::default(),
|
||||
header_props,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -544,19 +544,188 @@ video {
|
||||
--tw-backdrop-sepia: ;
|
||||
}
|
||||
|
||||
.m-3 {
|
||||
margin: 0.75rem;
|
||||
}
|
||||
|
||||
.m-1 {
|
||||
margin: 0.25rem;
|
||||
}
|
||||
|
||||
.my-3 {
|
||||
margin-top: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.my-5 {
|
||||
margin-top: 1.25rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.my-8 {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.my-10 {
|
||||
margin-top: 2.5rem;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.my-12 {
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.mx-4 {
|
||||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.mb-3 {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.inline {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.h-0 {
|
||||
height: 0px;
|
||||
}
|
||||
|
||||
.min-h-full {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.content-end {
|
||||
align-content: flex-end;
|
||||
}
|
||||
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.border-blue-200 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(191 219 254 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.bg-blue-50 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 246 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-blue-800 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(30 64 175 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.p-3 {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.p-2 {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.px-2 {
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.px-4 {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.px-5 {
|
||||
padding-left: 1.25rem;
|
||||
padding-right: 1.25rem;
|
||||
}
|
||||
|
||||
.pr-2 {
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.pr-3 {
|
||||
padding-right: 0.75rem;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
.text-base {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
.text-2xl {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.font-medium {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.text-gray-600 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(75 85 99 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-blue-700 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(29 78 216 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-blue-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(30 64 175 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-gray-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(31 41 55 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
.filter {
|
||||
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);
|
||||
}
|
||||
|
||||
.after\:content-\[\'\2c \'\]::after {
|
||||
--tw-content: ',';
|
||||
content: var(--tw-content);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/m-logo.svg" />
|
||||
<link rel="icon" type="image/png" href="/m-logo-192.png" />
|
||||
</head>
|
||||
<body>
|
||||
<body class="bg-blue-50">
|
||||
{% include "site_header.html" %}
|
||||
{% block content %} Placeholder {% endblock %}
|
||||
{# footer, should be not dependant on the each individual handler but it should have it's own handler #}
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
<ul>
|
||||
{% for post in posts %}
|
||||
<li>
|
||||
<li class="my-12">
|
||||
{% include "post_preview_card.html" %} {#
|
||||
<ArticlePreviewCard {article} {segment} /> #} {#
|
||||
<ArticleFooter {article} {segment} /> #}
|
||||
|
@ -1,27 +1,29 @@
|
||||
<article>
|
||||
<header>
|
||||
<h2>
|
||||
<header class="px-4 mb-3">
|
||||
<h2 class="text-2xl">
|
||||
<a rel="prefetch" href="/blog/{{post.slug}}">{{post.metadata.title}}</a>
|
||||
</h2>
|
||||
<section class="created-at m-1 text-right text-sm text-gray-600">
|
||||
<span>Published on</span>
|
||||
<time datetime="{post.metadata.date}"> {{post.metadata.date}} </time>
|
||||
</section>
|
||||
</header>
|
||||
{# article preview, maybe implement as a filter? #}
|
||||
</article>
|
||||
|
||||
<footer>
|
||||
<div class="article-tags">
|
||||
<p class="px-5 text-gray-800"> TODO: article preview, maybe implement as a filter?,
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Ald </p>
|
||||
<hr class="my-3 mx-4 h-0 border-blue-200 bg-blue-800 text-blue-800" />
|
||||
<footer class="px-4">
|
||||
<section class="article-tags text-base">
|
||||
{% if post.metadata.tags.len() > 0 %}
|
||||
<span class="lighten">Tags:</span>
|
||||
<ul>
|
||||
<span class="text-gray-600">Tags:</span>
|
||||
<ul class="inline">
|
||||
{% for tag in post.metadata.tags %}
|
||||
<li class="{tagsListLiClass}">
|
||||
<li class="inline italic text-blue-700 {% if !loop.last %} after:content-[','] {% endif %}">
|
||||
<a href="/blog/tags/{{tag}}">{{tag}}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="created-at">
|
||||
<span>Published on</span>
|
||||
<time datetime="{post.metadata.date}"> {{post.metadata.date}} </time>
|
||||
</div>
|
||||
</footer>
|
||||
</footer>
|
||||
</article>
|
||||
|
@ -1,31 +1,19 @@
|
||||
<header class="min-h-full bg-blue-50">
|
||||
<nav class="">
|
||||
<section>
|
||||
<ul class="">
|
||||
{% for link in header_props.links %}
|
||||
<li>
|
||||
<nav class="flex">
|
||||
{% match header_props.back_link %}
|
||||
{% when Some with (link) %}
|
||||
<a
|
||||
rel="prefetch"
|
||||
class=""
|
||||
class="p-3 text-lg font-medium drop-shadow-md"
|
||||
href="{{link.href}}"
|
||||
>
|
||||
{{link.label}}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<aside class="logo-section ">
|
||||
<a class="logo" href=".">
|
||||
<img
|
||||
class=""
|
||||
src="/m-logo.svg"
|
||||
alt="m logo"
|
||||
width="44px"
|
||||
height="44px"
|
||||
/>
|
||||
{% 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>
|
||||
</section>
|
||||
</nav>
|
||||
</header>
|
||||
|
Loading…
Reference in New Issue
Block a user