feat(css): co-locate block CSS with auto-discovery via Tailwind pipeline

Move block-specific CSS from centralized assets/css/blox/ to co-located
style.css files within each block directory. The css.html partial now
auto-discovers style.css files from bundled and community blocks and
concatenates them into the Tailwind entry via resources.Concat, enabling
full @apply support while avoiding VFS path resolution issues.

Shared biography styles (avatar, ruby) are extracted to blox/shared/css/
and auto-included before per-block styles. Navbar CSS moves to
components/ as it's a framework-level concern. The separate community
CSS pipeline in site_head.html is removed — all block CSS now flows
through a single Tailwind entry point.
This commit is contained in:
George Cushen committed 2026-03-27 04:15:49 +00:00
1 parent 13dde12ae0
commit 4fa40b91e2
10 files changed
+206 -318

No files matched your search

-3
View File
@@ -1,3 +0,0 @@
@import "biography.css";
@import "skills.css";
@import "navbar.css";
-281
View File
@@ -1,281 +0,0 @@
.resume-biography {
#profile {
text-align: center;
padding: 30px 10px;
position: relative;
}
/* Custom avatar size on homepage - size controlled by template params */
.avatar {
width: auto;
height: auto;
object-fit: cover;
}
.network-icon {
display: inline-flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
list-style: none;
padding: 0;
margin: 0;
}
#profile .network-icon {
margin-top: 0.5rem;
}
.network-icon li {
margin-right: 10px;
@media (max-width: 640px) {
/* Min 48px Lighthouse tap targets on mobile */
margin-right: 12px;
}
}
.network-icon li:last-of-type {
margin-right: 0;
}
.network-icon li:hover {
transform: scale(1.2);
}
.big-icon {
font-size: 2rem;
}
.ul-interests li {
font-size: 0.9rem;
}
.ul-edu li .description p.course {
font-size: 0.9rem;
}
.ul-edu li .description p.institution {
font-size: 0.75rem;
}
.network-icon .big-icon {
font-size: 1.5rem;
}
.bio-text {
max-width: 680px;
}
}
/* Modern Biography Block Enhancements */
.resume-biography-modern {
/* Ensure proper stacking context for gradients and effects */
position: relative;
z-index: 1;
}
/* Ruby pronunciation styling - 2026+ modern design */
.hb-ruby-name {
ruby-position: over;
display: ruby;
text-align: center;
}
.hb-ruby-name rt {
/* Elegant, subdued annotation text */
font-size: 0.45em;
font-weight: var(--hb-font-weight-body-bold, 600);
letter-spacing: 0.08em;
line-height: 1.2;
/* Subtle gradient text for premium feel */
background: linear-gradient(135deg, var(--color-gray-500) 0%, var(--color-gray-400) 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
/* Smooth reveal animation */
opacity: 0.85;
transition:
opacity 0.3s ease,
transform 0.3s ease;
}
.dark .hb-ruby-name rt {
background: linear-gradient(135deg, var(--color-gray-400) 0%, var(--color-gray-300) 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
/* Hover enhancement - subtle lift */
.hb-ruby-name:hover rt {
opacity: 1;
transform: translateY(-1px);
}
/* CJK-optimized variant - larger annotation for CJK characters over Latin */
.hb-ruby-name--cjk rt {
font-size: 0.5em;
font-weight: 400;
letter-spacing: 0.12em;
}
/* Latin-optimized variant - smaller annotation for romanization over CJK */
.hb-ruby-name--latin rt {
font-size: 0.4em;
font-weight: var(--hb-font-weight-body-bold, 600);
text-transform: none;
}
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.hb-ruby-name rt {
transition: none;
}
.hb-ruby-name:hover rt {
transform: none;
}
}
/* Fallback styling for browsers without ruby support */
@supports not (display: ruby) {
.hb-ruby-name {
display: inline-block;
}
.hb-ruby-name rt {
display: block;
font-size: 0.6em;
margin-bottom: 0.25em;
}
}
/* Legacy class support */
.ruby-modern {
ruby-position: over;
}
.ruby-modern rt {
font-size: 0.7em;
opacity: 0.8;
letter-spacing: 0.05em;
}
/* Enhanced backdrop blur support */
@supports (backdrop-filter: blur(10px)) {
.resume-biography-modern [class*="backdrop-blur"] {
backdrop-filter: blur(10px);
}
}
/* Smooth animations for all interactive elements */
.resume-biography-modern * {
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
/* Enhanced focus states for accessibility */
.resume-biography-modern a:focus-visible {
outline: 2px solid var(--color-primary-500);
outline-offset: 2px;
border-radius: 0.5rem;
}
/* Improved text selection styling */
.resume-biography-modern ::selection {
background-color: color-mix(in oklch, var(--color-primary-200) 30%, transparent);
color: var(--color-primary-900);
}
.dark .resume-biography-modern ::selection {
background-color: color-mix(in oklch, var(--color-primary-800) 30%, transparent);
color: var(--color-primary-100);
}
/* Enhanced gradient text support */
.resume-biography-modern .bg-clip-text {
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
/* Ensure text remains readable on fallback */
@supports not (background-clip: text) {
.resume-biography-modern .bg-clip-text {
color: var(--color-neutral-900);
}
.dark .resume-biography-modern .bg-clip-text {
color: var(--color-neutral-100);
}
}
.avatar-wrapper {
position: relative;
/* width and height controlled by template params */
margin-left: auto;
margin-right: auto;
}
.avatar-emoji {
position: absolute;
width: 40px;
height: 40px;
line-height: 40px;
border-radius: 100%;
bottom: 0;
right: 0;
text-align: center;
font-size: 20px;
background-color: #fff;
color: #000;
/* override parent alpha */
box-shadow:
0 10px 20px rgb(0 0 0 / 4%),
0 2px 6px rgb(0 0 0 / 4%),
0 0 1px rgb(0 0 0 / 4%);
}
.dark .avatar-emoji {
background-color: #000;
box-shadow:
0 10px 20px rgb(0 0 0 / 4%),
0 2px 6px rgb(0 0 0 / 4%),
0 0 1px rgb(255 255 255 / 96%);
}
/* Biography Block */
.hbx-bg-gradient {
position: relative;
isolation: isolate;
}
.hbx-bg-gradient::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
background-attachment: fixed;
transition: opacity 0.5s ease-in-out;
opacity: 1;
}
/* Light Mode Gradient */
.hbx-bg-gradient::before {
background-image:
radial-gradient(at 20% 25%, hsla(212, 80%, 95%, 1) 0px, transparent 50%), radial-gradient(at 75% 30%, hsla(160, 80%, 95%, 1) 0px, transparent 50%),
radial-gradient(at 25% 80%, hsla(280, 80%, 95%, 1) 0px, transparent 50%), radial-gradient(at 75% 85%, hsla(30, 80%, 95%, 1) 0px, transparent 50%);
}
/* Dark Mode Gradient */
.dark .hbx-bg-gradient::before {
background-image:
radial-gradient(at 20% 25%, hsla(212, 35%, 15%, 1) 0px, transparent 50%), radial-gradient(at 75% 30%, hsla(160, 35%, 15%, 1) 0px, transparent 50%),
radial-gradient(at 25% 80%, hsla(280, 35%, 15%, 1) 0px, transparent 50%), radial-gradient(at 75% 85%, hsla(30, 35%, 15%, 1) 0px, transparent 50%);
}
@@ -10,3 +10,4 @@
@import "author-notes.css";
@import "glassmorphism.css";
@import "cover.css";
@import "navbar.css";
+3 -1
View File
@@ -34,8 +34,10 @@
/* Hugo Blox module components */
@import "components/all.css";
@import "blox/all.css";
@import "views/all.css";
/* Block-specific CSS is auto-discovered from co-located style.css files */
/* See css.html partial for the discovery mechanism */
@import "chroma.css";
/* Hugo Blox Search */
@@ -0,0 +1,63 @@
.resume-biography {
#profile {
text-align: center;
padding: 30px 10px;
position: relative;
}
.avatar {
width: auto;
height: auto;
object-fit: cover;
}
.network-icon {
display: inline-flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
list-style: none;
padding: 0;
margin: 0;
}
#profile .network-icon {
margin-top: 0.5rem;
}
.network-icon li {
margin-right: 10px;
@media (max-width: 640px) {
margin-right: 12px;
}
}
.network-icon li:last-of-type {
margin-right: 0;
}
.network-icon li:hover {
transform: scale(1.2);
}
.big-icon {
font-size: 2rem;
}
.ul-interests li {
font-size: 0.9rem;
}
.ul-edu li .description p.course {
font-size: 0.9rem;
}
.ul-edu li .description p.institution {
font-size: 0.75rem;
}
.network-icon .big-icon {
font-size: 1.5rem;
}
}
@@ -1,36 +1,14 @@
/*************************************************
* Hugo Blox: Skills
**************************************************/
/* MOVED TO HTML */
/* .skill-group-title { */
/* display: inline-block; */
/* margin-bottom: 15px; */
/* padding-bottom: 5px; */
/* border-bottom: 2px solid #c7c7c7; */
/* font-size: 1rem; */
/* line-height: 30px; */
/* font-weight: 600; */
/* text-transform: uppercase; */
/* letter-spacing: 1px; */
/* } */
.skills-content {
margin-bottom: 15px;
}
.skills-name {
margin-bottom: 6px;
/* color: #888; */ /* Inaccessible */
font-size: 1rem;
letter-spacing: 1px;
text-transform: uppercase;
}
/* .dark .skills-name { */
/* color: #eee; */
/* } */
.skills-description {
margin-bottom: 6px;
font-size: 0.7rem;
@@ -0,0 +1,122 @@
/* Shared styles for biography-family blocks (resume-biography, resume-biography-3) */
.avatar-wrapper {
position: relative;
/* width and height controlled by template params */
margin-left: auto;
margin-right: auto;
}
.avatar-emoji {
position: absolute;
width: 40px;
height: 40px;
line-height: 40px;
border-radius: 100%;
bottom: 0;
right: 0;
text-align: center;
font-size: 20px;
background-color: #fff;
color: #000;
/* override parent alpha */
box-shadow:
0 10px 20px rgb(0 0 0 / 4%),
0 2px 6px rgb(0 0 0 / 4%),
0 0 1px rgb(0 0 0 / 4%);
}
.dark .avatar-emoji {
background-color: #000;
box-shadow:
0 10px 20px rgb(0 0 0 / 4%),
0 2px 6px rgb(0 0 0 / 4%),
0 0 1px rgb(255 255 255 / 96%);
}
/* Ruby pronunciation styling */
.hb-ruby-name {
ruby-position: over;
display: ruby;
text-align: center;
}
.hb-ruby-name rt {
font-size: 0.45em;
font-weight: var(--hb-font-weight-body-bold, 600);
letter-spacing: 0.08em;
line-height: 1.2;
background: linear-gradient(135deg, var(--color-gray-500) 0%, var(--color-gray-400) 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
opacity: 0.85;
transition:
opacity 0.3s ease,
transform 0.3s ease;
}
.dark .hb-ruby-name rt {
background: linear-gradient(135deg, var(--color-gray-400) 0%, var(--color-gray-300) 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.hb-ruby-name:hover rt {
opacity: 1;
transform: translateY(-1px);
}
/* CJK-optimized variant */
.hb-ruby-name--cjk rt {
font-size: 0.5em;
font-weight: 400;
letter-spacing: 0.12em;
}
/* Latin-optimized variant */
.hb-ruby-name--latin rt {
font-size: 0.4em;
font-weight: var(--hb-font-weight-body-bold, 600);
text-transform: none;
}
@media (prefers-reduced-motion: reduce) {
.hb-ruby-name rt {
transition: none;
}
.hb-ruby-name:hover rt {
transform: none;
}
}
@supports not (display: ruby) {
.hb-ruby-name {
display: inline-block;
}
.hb-ruby-name rt {
display: block;
font-size: 0.6em;
margin-bottom: 0.25em;
}
}
/* Legacy class support */
.ruby-modern {
ruby-position: over;
}
.ruby-modern rt {
font-size: 0.7em;
opacity: 0.8;
letter-spacing: 0.05em;
}
.bio-text {
max-width: 680px;
}
+17 -1
View File
@@ -3,8 +3,24 @@
{{/* Build dynamic @source list and prepend it before main.css */}}
{{ $dynamic_sources_str := partial "tailwind_sources.html" . }}
{{ $dynamic_sources_res := resources.FromString "css/_dynamic_sources.css" $dynamic_sources_str }}
{{ $main_css := . }}
{{ $entry := slice $dynamic_sources_res $main_css | resources.Concat "css/_entry.css" }}
{{ $entry_parts := slice $dynamic_sources_res $main_css }}
{{/* Auto-discover co-located block CSS: shared utilities first, then per-block styles */}}
{{ range resources.Match "css/hbx/blocks/shared/**/*.css" }}
{{ $entry_parts = $entry_parts | append . }}
{{ end }}
{{ range resources.Match "css/hbx/blocks/**/style.css" }}
{{ $entry_parts = $entry_parts | append . }}
{{ end }}
{{/* Community block CSS (mounted by templates into dist/community/blox/) */}}
{{ range resources.Match "dist/community/blox/**/*.css" }}
{{ $entry_parts = $entry_parts | append . }}
{{ end }}
{{ $entry := $entry_parts | resources.Concat "css/_entry.css" }}
{{ $opts := dict "minify" (not hugo.IsDevelopment) }}
{{ with $entry | css.TailwindCSS $opts }}
{{ if hugo.IsDevelopment }}
@@ -70,16 +70,6 @@
{{ partial "css.html" . }}
{{ end }}
{{/* Load community blox styles */}}
{{ $hb_community_styles := resources.Match "dist/community/blox/**.css" }}
{{ with $hb_community_styles }}
{{ $hb_community_styles = $hb_community_styles | resources.Concat "css/community-hugo-blox.css" }}
{{- if hugo.IsProduction -}}
{{- $hb_community_styles = $hb_community_styles | minify | fingerprint "sha256" -}}
{{- end -}}
<link href="{{ $hb_community_styles.RelPermalink }}" rel="stylesheet" />
{{ end }}
{{ if fileExists "assets/css/custom.css" }}
{{ $styles := resources.Get "css/custom.css" | minify | fingerprint "sha256" }}
<link href="{{ $styles.RelPermalink }}" rel="stylesheet" />