@@ -13,14 +13,21 @@ export interface Props {
1313 eyebrow? : string ;
1414 heading? : string ;
1515 body? : string ;
16+ bgColor? : string ;
1617}
1718
1819const {
1920 eyebrow,
2021 heading = ' Creative project design for social good champions.' ,
2122 body,
23+ bgColor,
2224} = Astro .props ;
2325
26+ // On the phase-gradient backdrop, text and icons sit on a coloured band so
27+ // they go solid white. On any lighter background (default `white`) they pick
28+ // up the phase-gradient themselves so the section keeps its visual identity.
29+ const onGradientBg = bgColor === ' phase-gradient' ;
30+
2431const skillsRaw = await getCollection (' skills' );
2532const skills = skillsRaw
2633 .filter ((e ) => isVisible (e ))
@@ -47,16 +54,59 @@ const cloudSkills: SkillsCloudSkill[] = skills.map((s) => ({
4754---
4855
4956<div
50- class =" relative overflow-hidden md:grid md:grid-cols-2 md:items-center md:gap-16"
57+ class:list ={ [
58+ ' relative overflow-hidden md:grid md:grid-cols-2 md:items-center md:gap-16' ,
59+ onGradientBg ? ' skills-cloud-section-dark' : ' skills-cloud-section-light' ,
60+ ]}
5161>
62+ {
63+ ! onGradientBg && (
64+ <svg
65+ aria-hidden = " true"
66+ focusable = " false"
67+ width = " 0"
68+ height = " 0"
69+ class = " pointer-events-none absolute"
70+ style = " position:absolute;width:0;height:0"
71+ >
72+ <defs >
73+ <linearGradient
74+ id = " skills-cloud-phase-gradient"
75+ x1 = " 0"
76+ y1 = " 0"
77+ x2 = " 1"
78+ y2 = " 0"
79+ >
80+ <stop
81+ offset = " 0%"
82+ style = " stop-color: var(--color-phase-understand)"
83+ />
84+ <stop
85+ offset = " 33%"
86+ style = " stop-color: var(--color-phase-define)"
87+ />
88+ <stop
89+ offset = " 66%"
90+ style = " stop-color: var(--color-phase-deliver)"
91+ />
92+ <stop
93+ offset = " 100%"
94+ style = " stop-color: var(--color-phase-sustain)"
95+ />
96+ </linearGradient >
97+ </defs >
98+ </svg >
99+ )
100+ }
101+
52102 { /* Single SkillsCloud island. On mobile: absolute backdrop behind the
53103 heading (decorative, opacity-15). On desktop: in-flow grid column 1
54104 alongside the heading. `md:relative md:z-10` is load-bearing — without
55105 a stacking context above siblings here, hover hit-tests don't reach the
56106 cloud on desktop and the mousemove-driven 3D effect dies. */ }
57107 <div
58108 data-skills-cloud-root
59- class =" skills-cloud-inverted pointer-events-none absolute top-0 block h-96 w-lg opacity-15 md:pointer-events-auto md:relative md:z-10 md:col-start-1 md:h-auto md:w-auto md:opacity-100"
109+ class =" pointer-events-none absolute top-0 block h-96 w-lg opacity-15 md:pointer-events-auto md:relative md:z-10 md:col-start-1 md:h-auto md:w-auto md:opacity-100"
60110 >
61111 <div class =" skills-cloud-wrap h-auto" >
62112 <SkillsCloud client:visible skills ={ cloudSkills } />
@@ -68,20 +118,36 @@ const cloudSkills: SkillsCloudSkill[] = skills.map((s) => ({
68118 >
69119 {
70120 eyebrow && (
71- <p class = " mb-3 text-xs font-medium tracking-wide text-white/85 uppercase" >
121+ <p
122+ class :list = { [
123+ ' mb-3 text-xs font-medium tracking-wide uppercase' ,
124+ onGradientBg ? ' text-white/85' : ' text-ink-soft' ,
125+ ]}
126+ >
72127 { eyebrow }
73128 </p >
74129 )
75130 }
76131 <Heading
77132 level ={ 2 }
78- class =" pb-2 text-balance text-white"
133+ class:list ={ [
134+ ' pb-2 text-balance' ,
135+ onGradientBg
136+ ? ' text-white'
137+ : ' gradient-phase bg-clip-text text-transparent' ,
138+ ]}
79139 >
80140 { heading }
81141 </Heading >
82142 {
83143 body && (
84- <Text size = " lg" class = " mt-4 text-white/85" >
144+ <Text
145+ size = " lg"
146+ class :list = { [
147+ ' mt-4' ,
148+ onGradientBg ? ' text-white/85' : ' text-ink-soft' ,
149+ ]}
150+ >
85151 { body }
86152 </Text >
87153 )
@@ -94,10 +160,26 @@ const cloudSkills: SkillsCloudSkill[] = skills.map((s) => ({
94160</div >
95161
96162<style is:global >
97- .skills-cloud-inverted .skills-cloud__icon {
163+ .skills-cloud-section-dark .skills-cloud__icon {
98164 color: var(--color-cream, #fff);
99165 }
100166
167+ /* Light backdrop: paint icons with the phase-gradient via the SVG
168+ <defs> emitted above. Hugeicons are stroke-based with `currentColor`,
169+ so we point both stroke and fill at the gradient and zero out
170+ currentColor as a safety net. */
171+ .skills-cloud-section-light .skills-cloud__icon {
172+ color: transparent;
173+ }
174+ .skills-cloud-section-light .skills-cloud__icon svg,
175+ .skills-cloud-section-light .skills-cloud__icon svg * {
176+ stroke: url(#skills-cloud-phase-gradient);
177+ }
178+ .skills-cloud-section-light .skills-cloud__icon svg [fill]:not([fill='none']),
179+ .skills-cloud-section-light .skills-cloud__icon svg :not([fill])[stroke='none'] {
180+ fill: url(#skills-cloud-phase-gradient);
181+ }
182+
101183 .skills-cloud-wrap {
102184 position: relative;
103185 width: 100%;
0 commit comments