From 4fa40b91e214e23ad7458234bd23bb4967fd1b1c Mon Sep 17 00:00:00 2001 From: George Cushen Date: Fri, 27 Mar 2026 04:15:49 +0000 Subject: [PATCH] feat(css): co-locate block CSS with auto-discovery via Tailwind pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- modules/blox/assets/css/blox/all.css | 3 - modules/blox/assets/css/blox/biography.css | 281 ------------------ modules/blox/assets/css/components/all.css | 1 + .../css/{blox => components}/navbar.css | 0 modules/blox/assets/css/main.css | 4 +- modules/blox/blox/resume-biography/style.css | 63 ++++ .../resume-skills/style.css} | 22 -- .../blox/blox/shared/css/biography-shared.css | 122 ++++++++ modules/blox/layouts/_partials/css.html | 18 +- modules/blox/layouts/_partials/site_head.html | 10 - 10 files changed, 206 insertions(+), 318 deletions(-) delete mode 100644 modules/blox/assets/css/blox/all.css delete mode 100644 modules/blox/assets/css/blox/biography.css rename modules/blox/assets/css/{blox => components}/navbar.css (100%) create mode 100644 modules/blox/blox/resume-biography/style.css rename modules/blox/{assets/css/blox/skills.css => blox/resume-skills/style.css} (51%) create mode 100644 modules/blox/blox/shared/css/biography-shared.css diff --git a/modules/blox/assets/css/blox/all.css b/modules/blox/assets/css/blox/all.css deleted file mode 100644 index 7936878d..00000000 --- a/modules/blox/assets/css/blox/all.css +++ /dev/null @@ -1,3 +0,0 @@ -@import "biography.css"; -@import "skills.css"; -@import "navbar.css"; diff --git a/modules/blox/assets/css/blox/biography.css b/modules/blox/assets/css/blox/biography.css deleted file mode 100644 index d1a03f44..00000000 --- a/modules/blox/assets/css/blox/biography.css +++ /dev/null @@ -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%); -} diff --git a/modules/blox/assets/css/components/all.css b/modules/blox/assets/css/components/all.css index 8869f149..fdc86ffe 100644 --- a/modules/blox/assets/css/components/all.css +++ b/modules/blox/assets/css/components/all.css @@ -10,3 +10,4 @@ @import "author-notes.css"; @import "glassmorphism.css"; @import "cover.css"; +@import "navbar.css"; diff --git a/modules/blox/assets/css/blox/navbar.css b/modules/blox/assets/css/components/navbar.css similarity index 100% rename from modules/blox/assets/css/blox/navbar.css rename to modules/blox/assets/css/components/navbar.css diff --git a/modules/blox/assets/css/main.css b/modules/blox/assets/css/main.css index b7157579..503a7d9c 100644 --- a/modules/blox/assets/css/main.css +++ b/modules/blox/assets/css/main.css @@ -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 */ diff --git a/modules/blox/blox/resume-biography/style.css b/modules/blox/blox/resume-biography/style.css new file mode 100644 index 00000000..e744feb5 --- /dev/null +++ b/modules/blox/blox/resume-biography/style.css @@ -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; + } +} diff --git a/modules/blox/assets/css/blox/skills.css b/modules/blox/blox/resume-skills/style.css similarity index 51% rename from modules/blox/assets/css/blox/skills.css rename to modules/blox/blox/resume-skills/style.css index 21542e5d..d68d9a9d 100644 --- a/modules/blox/assets/css/blox/skills.css +++ b/modules/blox/blox/resume-skills/style.css @@ -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; diff --git a/modules/blox/blox/shared/css/biography-shared.css b/modules/blox/blox/shared/css/biography-shared.css new file mode 100644 index 00000000..7fb1a56c --- /dev/null +++ b/modules/blox/blox/shared/css/biography-shared.css @@ -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; +} diff --git a/modules/blox/layouts/_partials/css.html b/modules/blox/layouts/_partials/css.html index 422dd1a8..8a694233 100644 --- a/modules/blox/layouts/_partials/css.html +++ b/modules/blox/layouts/_partials/css.html @@ -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 }} diff --git a/modules/blox/layouts/_partials/site_head.html b/modules/blox/layouts/_partials/site_head.html index b8f5c5a4..1585d2ce 100644 --- a/modules/blox/layouts/_partials/site_head.html +++ b/modules/blox/layouts/_partials/site_head.html @@ -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 -}} - - {{ end }} - {{ if fileExists "assets/css/custom.css" }} {{ $styles := resources.Get "css/custom.css" | minify | fingerprint "sha256" }}