]> danix's work - danix.xyz-2.git/commitdiff
Implement article type colors, reading progress bar, and optimize layout
authorDanilo M. <redacted>
Wed, 15 Apr 2026 21:39:11 +0000 (23:39 +0200)
committerDanilo M. <redacted>
Wed, 15 Apr 2026 21:39:11 +0000 (23:39 +0200)
- Add colored type pills to article list items (overlay on thumbnails, top-right)
- Add type accent corner (L-shaped) to single article headers with colored pill badge in metadata
- Implement smooth reading progress bar at viewport top (violet→green gradient)
- Progress bar only appears on single pages/articles, not on lists or homepage
- Constrain article width to max-w-7xl for comfortable reading with side gutters
- Use CSS variables for all colors to support dark/light theme switching
- Add reading-progress.js with requestAnimationFrame throttling for performance
- Update HANDOFF.md with current progress

Co-Authored-By: Claude Haiku 4.5 <redacted>
18 files changed:
HANDOFF.md
content/en/articles/about-loneliness/index.md [new file with mode: 0644]
content/en/articles/about-loneliness/loneliness.jpg [new file with mode: 0644]
content/en/articles/cad-designing-in-gnu-linux/2021-04-25-183336_1366x768_scrot.png [new file with mode: 0644]
content/en/articles/cad-designing-in-gnu-linux/2021-04-25-190246_1366x768_scrot.png [new file with mode: 0644]
content/en/articles/cad-designing-in-gnu-linux/PXL_20210501_114542849.jpg [new file with mode: 0644]
content/en/articles/cad-designing-in-gnu-linux/index.md [new file with mode: 0644]
content/en/articles/cad-designing-in-gnu-linux/solvespace_demo.webm [new file with mode: 0644]
content/en/articles/gify-back-to-bash-scripting/G0092546.jpg [new file with mode: 0644]
content/en/articles/gify-back-to-bash-scripting/index.md
content/en/articles/gify-back-to-bash-scripting/piscaturi.gif [new file with mode: 0644]
themes/danix-xyz-hacker/assets/css/main.min.css
themes/danix-xyz-hacker/assets/js/reading-progress.js [new file with mode: 0644]
themes/danix-xyz-hacker/layouts/_default/baseof.html
themes/danix-xyz-hacker/layouts/_default/single.html
themes/danix-xyz-hacker/layouts/articles/single.html
themes/danix-xyz-hacker/layouts/partials/article-header.html
themes/danix-xyz-hacker/layouts/partials/article-list-item.html

index a92dde36c0273453a930615d8ee79e2270aeb07a..68ff8a176e0ca05eb1e704407162be8eb5d3628c 100644 (file)
@@ -1,15 +1,19 @@
 Who this is for:
-  You are Danilo (danix), building a Hugo theme for danix.xyz - a bilingual (Italian/English) hacker-themed portfolio/blog site. You've completed the multilingual setup and are now finalizing the language switching mechanism and menu navigation.
+  You are Danilo (danix), building a Hugo theme for danix.xyz—a bilingual (Italian/English) hacker-themed portfolio/blog site. You have completed design system compliance work and are now implementing content features and fixing issues as you add articles to the site.
 
 What we covered:
-  The session focused on fixing the language switcher to maintain language context when navigating between pages. You identified that the menu links weren't respecting the current language, so clicking a menu item on an Italian page would take you to the English version. We analyzed
-  two solutions: a JavaScript workaround (hacky) versus Hugo's native language-specific menu configuration (idiomatic). We chose Option 1, refactoring the hugo.toml to define menus separately for each language using pageRef, which allows Hugo to automatically handle language prefixes. We removed the JavaScript files and updated the templates to access the language-aware menus.
+  Implemented a complete taxonomy system for tags and categories, enabling Hugo to automatically generate archive pages and term pages. Created thumbnail support for article listings with a redesigned card layout showing large images, titles, excerpts, and CTA buttons. Fixed image
+  and video shortcode path resolution to work correctly with Hugo page bundles by using the Resources API. Addressed front matter parameter handling in list templates when pinned parameters are undefined. Created an actions shortcode for download buttons and documented all shortcodes in SHORTCODES.md. Fixed the image shortcode to allow custom sizing by moving hardcoded w-full h-auto into the default class parameter. Added comprehensive CSS class reference documentation for image sizing, borders, shadows, and layout utilities.
 
 What was confirmed:
-  The language-specific menus are now fully functional. Italian pages display menu links with /it/ prefix (e.g., /it/articles/, /it/is/, /it/is/here/, /it/is/legal/) and Italian text (Articoli, Chi Sono, Contatti, Privacy). English pages display unprefixed links (e.g., /articles/, /is/, /is/here/, /is/legal/) with English text (Articles, About, Contact, Privacy). The language switcher buttons work correctly on both language versions. Hugo builds without errors. The desktop menu is hidden on mobile (hidden md:flex class) and visible on medium screens and above. The hamburger/mobile menu works on all viewports.
+  Taxonomies (tags and categories) are not yet fully functional, the archive pages (/tags/, /categories/) don't show the tags and individual term pages (/tags/example/) don't show the related articles. Article list items display as rich cards with thumbnail image, title link, date, type badge, excerpt, and "Read more" button—all optimized for page bundle resources. The Resources API correctly resolves image and video paths in page bundles to /articles/slug/filename.ext format. Front matter now uses image="filename.jpg" for images in page bundles, which the template automatically resolves. The image shortcode now respects custom sizing classes when provided in the class parameter. Video shortcode supports WebM, MP4, and Ogg formats. Quote and actions shortcodes have been created and documented. All shortcodes are documented in SHORTCODES.md with parameters, examples, and usage notes.
 
 Still in progress:
   Nothing was left open in this session.
 
 Next steps:
-  Hard refresh your browser to clear any cached files, then test the language switching and menu navigation thoroughly on both Italian and English pages using a desktop-width viewport to see the top menu. Once verified everything works as expected in the browser, commit all changes to git. The modified files are hugo.toml (new language-specific menu structure), header.html and hamburger-menu.html (simplified template logic), baseof.html (removed language-switcher.js script tag), and two JavaScript files have been deleted. Update your HANDOFF.md file to document the final architecture if needed.
\ No newline at end of file
+  Fix the taxonomy layouts for list pages and single taxonomy items.
+  Implement the colors for the different article types as defined in the guidelines.
+  Hard refresh the browser to clear cached CSS/JS files, then test the theme thoroughly: verify article listings display with thumbnails and correct styling across desktop and mobile, test navigation between articles and taxonomy pages, confirm all shortcodes (image, video, quote,
+  actions, gravatar, gallery, contact) render correctly in both dark and light modes, and validate that page bundle resources (images, videos) are served from the correct URLs. Once testing is complete and you're satisfied with the implementation, the content feature work is finished
+   and you can begin publishing more articles.
\ No newline at end of file
diff --git a/content/en/articles/about-loneliness/index.md b/content/en/articles/about-loneliness/index.md
new file mode 100644 (file)
index 0000000..204d520
--- /dev/null
@@ -0,0 +1,15 @@
++++
+title = "About Loneliness"
+date = "2011-08-11T18:09:41+00:00"
+draft = true
+type = "quote"
+image = "loneliness.jpg"
+author = "Danilo 'danix' M."
+excerpt = "again on loneliness, solitude and misantropy"
+categories = ["blogging", "quotes"]
+tags = ["loneliness", "solitude"]
++++
+
+{{< quote source="May Sarton" src="http://en.wikipedia.org/wiki/May_Sarton" >}}
+Loneliness is the poverty of self, solitude is the richness of self.
+{{< /quote >}}
diff --git a/content/en/articles/about-loneliness/loneliness.jpg b/content/en/articles/about-loneliness/loneliness.jpg
new file mode 100644 (file)
index 0000000..4939b77
Binary files /dev/null and b/content/en/articles/about-loneliness/loneliness.jpg differ
diff --git a/content/en/articles/cad-designing-in-gnu-linux/2021-04-25-183336_1366x768_scrot.png b/content/en/articles/cad-designing-in-gnu-linux/2021-04-25-183336_1366x768_scrot.png
new file mode 100644 (file)
index 0000000..d2bbad6
Binary files /dev/null and b/content/en/articles/cad-designing-in-gnu-linux/2021-04-25-183336_1366x768_scrot.png differ
diff --git a/content/en/articles/cad-designing-in-gnu-linux/2021-04-25-190246_1366x768_scrot.png b/content/en/articles/cad-designing-in-gnu-linux/2021-04-25-190246_1366x768_scrot.png
new file mode 100644 (file)
index 0000000..a1c49ec
Binary files /dev/null and b/content/en/articles/cad-designing-in-gnu-linux/2021-04-25-190246_1366x768_scrot.png differ
diff --git a/content/en/articles/cad-designing-in-gnu-linux/PXL_20210501_114542849.jpg b/content/en/articles/cad-designing-in-gnu-linux/PXL_20210501_114542849.jpg
new file mode 100644 (file)
index 0000000..d572567
Binary files /dev/null and b/content/en/articles/cad-designing-in-gnu-linux/PXL_20210501_114542849.jpg differ
diff --git a/content/en/articles/cad-designing-in-gnu-linux/index.md b/content/en/articles/cad-designing-in-gnu-linux/index.md
new file mode 100644 (file)
index 0000000..41920e7
--- /dev/null
@@ -0,0 +1,68 @@
++++
+title = "CAD designing in GNU/Linux"
+author = "Danilo M."
+type = "tech"
+date = "2021-05-01T17:51:58+00:00"
+excerpt = ""
+image = "PXL_20210501_114542849.jpg"
+categories = ["3D Printing", "diy"]
+tags = ["3D printing", "cad", "cura", "fusion360", "slicer", "solvespace"]
+
++++
+{{< quote source="semicit. Eminem" >}}
+       Guess who's back
+       back again
+       danix's back
+       tell your friends
+{{< /quote >}}
+
+So, I&#8217;m back at writing on this blog, it&#8217;s been a long time, I&#8217;ve been through quite some shitty times and a lot happened on a personal level that kept my attention away from blogging.
+
+One of the things that I&#8217;ve introduced in my life is a 3D printer, I&#8217;m the proud owner of an Ender3 Pro by creality.
+
+I&#8217;ve had it for a few months now and I&#8217;m pretty happy I purchased it, as it&#8217;s been a very helpful hobby, and even though I started this hobby using tools available for windows, I&#8217;m slowly moving back to GNU/Linux.
+
+<!--more-->
+
+## Slicing
+
+The software you mainly use with a 3D printer is a slicer, which is a piece of software that takes a 3D rendering of the item you want to print and slice it into many layers, stacked one on top of the other, you can manipulate the layers in various ways in order to alter the properties of the final printed item.
+
+{{< image class="max-w-m mx-auto" caption="Ultimaker Cura 4.9 main interface" src="2021-04-25-183336_1366x768_scrot.png" alt="Screenshot from Ultimaker Cura 4.9 main interface" >}}
+
+I&#8217;ve chosen [Ultimaker Cura](https://ultimaker.com/software/ultimaker-cura) as my main slicer, I started with it and I found it&#8217;s easy enough for a noob like me, but manages to give the user a lot of room for customization when you start understanding how it works.
+
+So far so good, Cura is available for windows, mac and GNU/Linux, so I went and downloaded the appImage package and I have to say, the loading time it&#8217;s even faster than on windows, I&#8217;ve added a few plugins and sliced quite a few files, and I&#8217;m very happy as to how it&#8217;s performing.
+
+## CAD drawing
+
+Now, everything would be good as it is, if you plan on using only the files you can find on the internet, which is a great deal, considering there&#8217;s sites like [thingiverse.com](https://www.thingiverse.com) or [myminifactory.com](https://www.myminifactory.com), where you can find litterally millions of files ready to be sliced and printed.
+
+Different story if you want to actually design a piece to be printed, in that case you&#8217;ll need a cad software.
+
+On windows my choice went immediately to Fusion360, not only it&#8217;s from Autodesk, the same company that authors AutoCad, the de-facto standard in the industry for mechanical cad, but the main reason is that Fusion360 offers a yearly free subscription for hobbyist and students.
+
+Another good reason for choosing Fusion360 is the huge amount of tutorials you can find on the web on how to approach cad design with it, it&#8217;s really great for beginners as it gives you an even easier learning curve.
+
+Sadly Fusion360 is not available on GNU/Linux, and that&#8217;s the main reason I&#8217;ve decided to look for a valid alternative. 
+
+Another very good reason for not using Fusion, is the fact that all of your files are uploaded to the cloud, and you are limited (in the free tier) to a maximum of 10 editable files at a time, which might seem a lot, but may easily become way too few depending on what are you working on. Anyway, after some searching, I&#8217;ve found a solution!!
+
+## Meet SolveSpace
+
+{{< image class="max-w-m mx-auto" caption="solvespace interface" src="2021-04-25-190246_1366x768_scrot.png" alt="Screenshot from solvespace interface" >}}
+
+[SolveSpace](https://solvespace.com/index.pl) is a 2D/3D parametric cad Software, it&#8217;s totally free, licensed under the GPLv3 and available for windows, mac and of course GNU/Linux.
+
+{{< video class="max-w-m mx-auto" autoplay="false" loop="false" src="/solvespace_demo.webm" alt="Solvespace video demo" >}}
+
+I&#8217;ve watched this introductory video and was able to pick it up and start designing in a matter of minutes, it&#8217;s very straightforward and even if it does things a bit differently than Fusion, it&#8217;s still very comfortable to use.
+
+The development is very active, this month was released the latest stable version which is 3.0 and I managed to compile it without any hassle on my Slackware64-current. Probably, once Slackware64 reaches the stable 15.0, I will push the slackbuild to the [SBo](https://slackbuilds.org) mailing list and see if they will make it available. In the mean time, if anyone is interested, just drop me a few lines in my socials and I'll help you as best as I can.
+
+## Slackware Package
+
+Since I've built the package for my use, why not sharing it with the world.
+I didn't submit it to SBo because I use git to retrieve the sources and that's not compliant with their guidelines.
+
+Thanks for passing by, and I&#8217;ll see you on the next one ;)
diff --git a/content/en/articles/cad-designing-in-gnu-linux/solvespace_demo.webm b/content/en/articles/cad-designing-in-gnu-linux/solvespace_demo.webm
new file mode 100644 (file)
index 0000000..4ef6cee
Binary files /dev/null and b/content/en/articles/cad-designing-in-gnu-linux/solvespace_demo.webm differ
diff --git a/content/en/articles/gify-back-to-bash-scripting/G0092546.jpg b/content/en/articles/gify-back-to-bash-scripting/G0092546.jpg
new file mode 100644 (file)
index 0000000..c8a5ae1
Binary files /dev/null and b/content/en/articles/gify-back-to-bash-scripting/G0092546.jpg differ
index 81de00ebf5082afdfca506d7cb4fbf8874e58e27..edf0f1e6dcf7b6613fadd59a9ff57e122ee00bcb 100644 (file)
@@ -1,13 +1,13 @@
 +++
 title = "gify.sh – back to bash scripting"
 author = "Danilo M."
-type = "article"
+type = "tech"
 date = "2016-01-25T12:41:06+00:00"
 image = "G0092546.jpg"
 categories = ["code", "diy", "fotografia"]
 tags = ["bash", "convert", "gif", "imagemagik", "mogrify", "script"]
 +++
-{{< image src="/uploads/2016/01/piscaturi.gif" alt="piccoli pescatori crescono" caption="gif image created using gify.sh" >}}
+{{< image class="max-w-lg mx-auto" src="piscaturi.gif" alt="piccoli pescatori crescono" caption="gif image created using gify.sh" >}}
 
 Today I'll present you a useful script that will help you create amazing gifs from your still photos using a couple tools from the [IMAGEMAGIK](https://www.imagemagick.org) suite, so without further ado, here it is, straight from github's gists.
 
diff --git a/content/en/articles/gify-back-to-bash-scripting/piscaturi.gif b/content/en/articles/gify-back-to-bash-scripting/piscaturi.gif
new file mode 100644 (file)
index 0000000..4e87d32
Binary files /dev/null and b/content/en/articles/gify-back-to-bash-scripting/piscaturi.gif differ
index 858df863267d84c729bf9a0c3ddcaccc74463aed..74161bc451b810e64293fc66afe85c9a4f701933 100644 (file)
@@ -1292,6 +1292,10 @@ button,
   position: fixed;
 }
 
+.absolute {
+  position: absolute;
+}
+
 .relative {
   position: relative;
 }
@@ -1304,14 +1308,30 @@ button,
   inset: 0px;
 }
 
+.bottom-0 {
+  bottom: 0px;
+}
+
+.left-0 {
+  left: 0px;
+}
+
 .right-0 {
   right: 0px;
 }
 
+.right-3 {
+  right: 0.75rem;
+}
+
 .top-0 {
   top: 0px;
 }
 
+.top-3 {
+  top: 0.75rem;
+}
+
 .z-10 {
   z-index: 10;
 }
@@ -1333,6 +1353,11 @@ button,
   margin-right: auto;
 }
 
+.my-6 {
+  margin-top: 1.5rem;
+  margin-bottom: 1.5rem;
+}
+
 .my-8 {
   margin-top: 2rem;
   margin-bottom: 2rem;
@@ -1370,6 +1395,17 @@ button,
   margin-top: 0.75rem;
 }
 
+.mt-4 {
+  margin-top: 1rem;
+}
+
+.line-clamp-3 {
+  overflow: hidden;
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 3;
+}
+
 .block {
   display: block;
 }
@@ -1390,6 +1426,14 @@ button,
   display: none;
 }
 
+.aspect-video {
+  aspect-ratio: 16 / 9;
+}
+
+.h-1 {
+  height: 0.25rem;
+}
+
 .h-32 {
   height: 8rem;
 }
@@ -1414,6 +1458,10 @@ button,
   min-height: calc(100vh - 200px);
 }
 
+.w-1 {
+  width: 0.25rem;
+}
+
 .w-32 {
   width: 8rem;
 }
@@ -1434,10 +1482,22 @@ button,
   max-width: 42rem;
 }
 
+.max-w-3xl {
+  max-width: 48rem;
+}
+
 .max-w-4xl {
   max-width: 56rem;
 }
 
+.max-w-7xl {
+  max-width: 80rem;
+}
+
+.max-w-lg {
+  max-width: 32rem;
+}
+
 .max-w-none {
   max-width: none;
 }
@@ -1467,6 +1527,10 @@ button,
   resize: none;
 }
 
+.resize {
+  resize: both;
+}
+
 .flex-col {
   flex-direction: column;
 }
@@ -1513,12 +1577,22 @@ button,
   margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
 }
 
+.space-y-3 > :not([hidden]) ~ :not([hidden]) {
+  --tw-space-y-reverse: 0;
+  margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse)));
+  margin-bottom: calc(0.75rem * var(--tw-space-y-reverse));
+}
+
 .space-y-6 > :not([hidden]) ~ :not([hidden]) {
   --tw-space-y-reverse: 0;
   margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
   margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
 }
 
+.overflow-hidden {
+  overflow: hidden;
+}
+
 .overflow-y-auto {
   overflow-y: auto;
 }
@@ -1609,6 +1683,10 @@ button,
   padding: 1rem;
 }
 
+.p-5 {
+  padding: 1.25rem;
+}
+
 .p-6 {
   padding: 1.5rem;
 }
@@ -1638,16 +1716,16 @@ button,
   padding-right: 2rem;
 }
 
-.py-0\.5 {
-  padding-top: 0.125rem;
-  padding-bottom: 0.125rem;
-}
-
 .py-1 {
   padding-top: 0.25rem;
   padding-bottom: 0.25rem;
 }
 
+.py-1\.5 {
+  padding-top: 0.375rem;
+  padding-bottom: 0.375rem;
+}
+
 .py-12 {
   padding-top: 3rem;
   padding-bottom: 3rem;
@@ -1676,6 +1754,10 @@ button,
   padding-left: 1.5rem;
 }
 
+.pt-2 {
+  padding-top: 0.5rem;
+}
+
 .pt-8 {
   padding-top: 2rem;
 }
@@ -1738,6 +1820,10 @@ button,
   font-style: italic;
 }
 
+.not-italic {
+  font-style: normal;
+}
+
 .leading-relaxed {
   line-height: 1.625;
 }
@@ -1814,6 +1900,10 @@ button,
   transition-duration: 150ms;
 }
 
+.duration-100 {
+  transition-duration: 100ms;
+}
+
 .duration-200 {
   transition-duration: 200ms;
 }
@@ -1992,6 +2082,10 @@ html.theme-light .prose-invert blockquote {
   color: var(--accent);
 }
 
+.hover\:underline:hover {
+  text-decoration-line: underline;
+}
+
 .hover\:opacity-80:hover {
   opacity: 0.8;
 }
@@ -2077,6 +2171,17 @@ html.theme-light .prose-invert blockquote {
   opacity: 0.5;
 }
 
+.group\/cta:hover .group-hover\/cta\:translate-x-1 {
+  --tw-translate-x: 0.25rem;
+  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));
+}
+
+.group:hover .group-hover\:scale-105 {
+  --tw-scale-x: 1.05;
+  --tw-scale-y: 1.05;
+  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));
+}
+
 .group:hover .group-hover\:text-accent {
   color: var(--accent);
 }
diff --git a/themes/danix-xyz-hacker/assets/js/reading-progress.js b/themes/danix-xyz-hacker/assets/js/reading-progress.js
new file mode 100644 (file)
index 0000000..ee1192f
--- /dev/null
@@ -0,0 +1,29 @@
+// Reading progress bar for single pages/articles
+(function() {
+  const progressBar = document.getElementById('reading-progress');
+
+  if (!progressBar) return;
+
+  function updateProgress() {
+    const windowHeight = window.innerHeight;
+    const documentHeight = document.documentElement.scrollHeight - windowHeight;
+    const scrollProgress = documentHeight > 0 ? (window.scrollY / documentHeight) * 100 : 0;
+    progressBar.style.width = scrollProgress + '%';
+  }
+
+  // Throttle the scroll event for better performance
+  let ticking = false;
+
+  window.addEventListener('scroll', function() {
+    if (!ticking) {
+      window.requestAnimationFrame(function() {
+        updateProgress();
+        ticking = false;
+      });
+      ticking = true;
+    }
+  }, false);
+
+  // Initial call
+  updateProgress();
+})();
index 28495625c3ef0c1d51f44b140cc918f1f387d6b9..7e8338a8e2031b76ba6de1b671d12d079ad42f20 100644 (file)
   <link rel="stylesheet" href="{{ $chroma.RelPermalink }}">
 </head>
 <body class="bg-bg text-text antialiased">
+  <!-- Reading progress bar (only on single pages/articles) -->
+  {{ if eq .Kind "page" }}
+  <div
+    id="reading-progress"
+    class="fixed top-0 left-0 h-1 transition-all duration-100"
+    style="width: 0%; background: linear-gradient(to right, var(--accent), var(--accent2)); z-index: 9999;"
+  ></div>
+  {{ end }}
   <!-- Skip to main content link -->
   <a href="#main" class="sr-only focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-accent focus:text-white focus:rounded">
     {{ i18n "skipToContent" }}
   <!-- Contact form script -->
   {{ $contactScript := resources.Get "js/contact-form.js" | minify }}
   <script src="{{ $contactScript.RelPermalink }}"></script>
+
+  <!-- Reading progress bar script (on single pages/articles) -->
+  {{ if eq .Kind "page" }}
+  {{ $progressScript := resources.Get "js/reading-progress.js" | minify }}
+  <script src="{{ $progressScript.RelPermalink }}"></script>
+  {{ end }}
 </body>
 </html>
index 60cf03d105554d0a0cbb1fbeb6faa9306b23583d..16d519f1001d8d261c8f260d41dcad4fa55a9c39 100644 (file)
@@ -1,6 +1,6 @@
 {{ define "main" }}
 <article class="mx-auto px-4 py-12">
-  <div class="grid md:grid-cols-3 gap-8">
+  <div class="grid md:grid-cols-3 gap-8 max-w-7xl mx-auto">
     <!-- Article section -->
     <div class="md:col-span-2">
       <!-- Article header -->
index c5d51e7916dc131794af4dd59d7134d9f4d5cad8..93abdb646b1fa45889af14615f767821e8aedad2 100644 (file)
@@ -2,7 +2,7 @@
 {{ $articleType := .Params.type | default "life" }}
 {{ $template := printf "article-types/%s.html" $articleType }}
 <article class="mx-auto px-4 py-12">
-  <div class="grid md:grid-cols-3 gap-8">
+  <div class="grid md:grid-cols-3 gap-8 max-w-7xl mx-auto">
     <!-- Article section -->
     <div class="md:col-span-2">
       <!-- Article header -->
index b7599096962f437fbe1094a2e8ef729f7c2318cc..a52f643e68a1440f474004c2702241f9f9b8b08c 100644 (file)
@@ -3,19 +3,28 @@
 {{ $typeData := index $typeConfig $articleType }}
 
 <div class="mb-8 pb-8 border-b border-border">
-  <!-- Type badge -->
-  {{ if $typeData }}
-  <span
-    class="inline-flex items-center px-3 py-1 rounded text-sm font-semibold mb-4 transition-colors type-{{ $articleType }}"
-  >
-    {{ i18n $articleType }}
-  </span>
-  {{ end }}
+  <!-- Title with accent corner -->
+  <div class="relative mb-6">
+    <!-- Vertical accent line (left side) -->
+    {{ if $typeData }}
+    <div
+      class="absolute left-0 top-0 bottom-0 w-1 rounded-full"
+      style="background-color: var(--type-{{ $articleType }});"
+    ></div>
+    {{ end }}
+
+    <!-- Horizontal accent line (bottom, 50% width with fade) -->
+    {{ if $typeData }}
+    <div
+      class="absolute left-0 bottom-0 h-1 rounded-full"
+      style="width: 50%; background: linear-gradient(to right, var(--type-{{ $articleType }}), var(--type-{{ $articleType }}) 0%, transparent 100%);"
+    ></div>
+    {{ end }}
 
-  <!-- Title -->
-  <h1 class="text-4xl md:text-5xl font-bold text-accent mb-4">
-    {{ .Title }}
-  </h1>
+    <h1 class="text-4xl md:text-5xl font-bold text-accent pl-6 pt-2">
+      {{ .Title }}
+    </h1>
+  </div>
 
   <!-- Metadata -->
   <div class="flex flex-wrap items-center gap-4 text-sm text-text-dim">
       <span>{{ .ReadingTime }} {{ i18n "min" }} {{ i18n "readingTime" }}</span>
     </div>
     {{ end }}
+
+    <!-- Type badge -->
+    {{ if $typeData }}
+    <span
+      class="inline-flex items-center px-3 py-1.5 rounded-full text-xs font-semibold text-white transition-colors"
+      style="background-color: var(--type-{{ $articleType }});"
+    >
+      {{ i18n $articleType }}
+    </span>
+    {{ end }}
   </div>
 </div>
index e4911256b0eff217e37a6e8935b705d0d887dee6..70d530c3aa1f72a87cc3a50e39d515a9c505b2a7 100644 (file)
 <article class="border border-border/30 rounded-lg overflow-hidden hover:border-accent/50 transition-all duration-200 group">
   <!-- Thumbnail -->
   {{ if $imageURL }}
-  <a href="{{ .RelPermalink }}" class="block overflow-hidden bg-surface/50" tabindex="-1">
+  <a href="{{ .RelPermalink }}" class="block overflow-hidden bg-surface/50 relative" tabindex="-1">
     <img
       src="{{ $imageURL }}"
       alt="{{ .Title }}"
       class="w-full aspect-video object-cover group-hover:scale-105 transition-transform duration-200"
       loading="lazy"
     />
+    <!-- Type badge pill overlay -->
+    {{ if $typeData }}
+    <div
+      class="absolute top-3 right-3 px-3 py-1.5 rounded-full text-xs font-semibold text-white transition-opacity"
+      style="background-color: var(--type-{{ $articleType }});"
+    >
+      {{ i18n $articleType }}
+    </div>
+    {{ end }}
   </a>
   {{ end }}
 
       <time datetime="{{ .PublishDate.Format "2006-01-02T15:04:05Z07:00" }}">
         {{ .PublishDate.Format "Jan 2, 2006" }}
       </time>
-
-      <!-- Type badge -->
-      {{ if $typeData }}
-      <span
-        class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium transition-colors type-{{ $articleType }}"
-      >
-        {{ i18n $articleType }}
-      </span>
-      {{ end }}
     </div>
 
     <!-- Excerpt -->