feat: updated styling and animations
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
7917801d45
commit
800b2864a6
10421
Solomons Website.pdf
Normal file
10421
Solomons Website.pdf
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 221 KiB |
72
demo-v1-chaotic.js
Normal file
72
demo-v1-chaotic.js
Normal file
@ -0,0 +1,72 @@
|
||||
// Version 1: More random and chaotic
|
||||
const staggerVisualizerEl = document.querySelector('.stagger-visualizer');
|
||||
const fragment = document.createDocumentFragment();
|
||||
const grid = [12, 6];
|
||||
const col = grid[0];
|
||||
const row = grid[1];
|
||||
const numberOfElements = col * row;
|
||||
|
||||
for (let i = 0; i < numberOfElements; i++) {
|
||||
let div = document.createElement('div');
|
||||
div.className = 'part';
|
||||
div.dataset.index = i;
|
||||
fragment.appendChild(div);
|
||||
}
|
||||
|
||||
staggerVisualizerEl.appendChild(fragment);
|
||||
|
||||
const staggersAnimation = anime.createTimeline({
|
||||
easing: 'easeInOutSine',
|
||||
loop: true,
|
||||
autoplay: false
|
||||
});
|
||||
|
||||
staggersAnimation
|
||||
// Chaotic burst outward with per-element randomness
|
||||
.add('.part', {
|
||||
translateX: (el) => anime.utils.random(-15, 15),
|
||||
translateY: (el) => anime.utils.random(-15, 15),
|
||||
rotate: (el) => anime.utils.random(-180, 180),
|
||||
scale: (el) => anime.utils.random(0.3, 0.8),
|
||||
duration: 800,
|
||||
easing: 'easeOutQuad',
|
||||
delay: anime.stagger(30, { grid: grid, from: 'center' })
|
||||
}, 0)
|
||||
// Floating upward with wave effect
|
||||
.add('.part', {
|
||||
translateY: (el, i) => -anime.utils.random(2, 8),
|
||||
rotate: (el) => anime.utils.random(-45, 45),
|
||||
duration: 1200,
|
||||
easing: 'easeInOutSine',
|
||||
delay: (el, i) => i * 20 + anime.utils.random(-100, 100)
|
||||
}, '-=400')
|
||||
// Scattered tumble
|
||||
.add('.part', {
|
||||
translateX: (el) => anime.utils.random(-20, 20),
|
||||
translateY: (el) => anime.utils.random(-20, 20),
|
||||
rotate: (el) => anime.utils.random(-360, 360),
|
||||
scale: (el) => anime.utils.random(0.5, 1.2),
|
||||
duration: 1000,
|
||||
easing: 'easeInOutQuad',
|
||||
delay: anime.stagger(40, { grid: grid, from: 'random' })
|
||||
})
|
||||
// Converge back with randomized paths
|
||||
.add('.part', {
|
||||
translateX: 0,
|
||||
translateY: 0,
|
||||
rotate: (el) => anime.utils.random(0, 360),
|
||||
scale: 1,
|
||||
duration: 1200,
|
||||
easing: 'easeOutElastic',
|
||||
delay: anime.stagger(50, { grid: grid, from: 'center' })
|
||||
})
|
||||
// Gentle pulse
|
||||
.add('.part', {
|
||||
scale: (el) => anime.utils.random(0.9, 1.1),
|
||||
opacity: (el) => anime.utils.random(0.5, 1),
|
||||
duration: 600,
|
||||
easing: 'easeInOutSine',
|
||||
delay: anime.stagger(30)
|
||||
}, '-=200');
|
||||
|
||||
staggersAnimation.play();
|
||||
72
demo-v2-fluid.js
Normal file
72
demo-v2-fluid.js
Normal file
@ -0,0 +1,72 @@
|
||||
// Version 2: Fluid and organic with floating effect
|
||||
const staggerVisualizerEl = document.querySelector('.stagger-visualizer');
|
||||
const fragment = document.createDocumentFragment();
|
||||
const grid = [12, 6];
|
||||
const col = grid[0];
|
||||
const row = grid[1];
|
||||
const numberOfElements = col * row;
|
||||
|
||||
for (let i = 0; i < numberOfElements; i++) {
|
||||
let div = document.createElement('div');
|
||||
div.className = 'part';
|
||||
div.dataset.index = i;
|
||||
fragment.appendChild(div);
|
||||
}
|
||||
|
||||
staggerVisualizerEl.appendChild(fragment);
|
||||
|
||||
const staggersAnimation = anime.createTimeline({
|
||||
easing: 'easeInOutSine',
|
||||
loop: true,
|
||||
autoplay: false
|
||||
});
|
||||
|
||||
staggersAnimation
|
||||
// Initial expansion with gentle stagger
|
||||
.add('.part', {
|
||||
translateX: anime.stagger((el, i) => anime.utils.random(-6, 6), { grid: grid, from: 'center', axis: 'x' }),
|
||||
translateY: anime.stagger((el, i) => anime.utils.random(-6, 6), { grid: grid, from: 'center', axis: 'y' }),
|
||||
scale: anime.stagger([0.7, 1.1], { grid: grid, from: 'center' }),
|
||||
duration: 1000,
|
||||
easing: 'easeOutElastic',
|
||||
delay: anime.stagger(50, { grid: grid, from: 'center' })
|
||||
}, 0)
|
||||
// Floating wave motion
|
||||
.add('.part', {
|
||||
translateY: (el, i) => anime.utils.random(-5, 5),
|
||||
rotate: (el) => anime.utils.random(-30, 30),
|
||||
opacity: [(el) => anime.utils.random(0.6, 0.8), 1],
|
||||
duration: 2000,
|
||||
easing: 'easeInOutSine',
|
||||
delay: (el, i) => i * 30 + anime.utils.random(0, 300)
|
||||
}, '-=500')
|
||||
// Gentle compression and expansion
|
||||
.add('.part', {
|
||||
scaleX: (el) => anime.utils.random(0.9, 1.05),
|
||||
scaleY: (el) => anime.utils.random(0.9, 1.05),
|
||||
duration: 1200,
|
||||
easing: 'easeInOutSine',
|
||||
delay: anime.stagger(40, { grid: grid, from: 'center' })
|
||||
}, '-=1000')
|
||||
// Slow drift
|
||||
.add('.part', {
|
||||
translateX: (el) => anime.utils.random(-3, 3),
|
||||
translateY: (el) => anime.utils.random(-3, 3),
|
||||
rotate: (el, i) => i % 2 === 0 ? 8 : -8,
|
||||
duration: 1500,
|
||||
easing: 'easeInOutQuad',
|
||||
delay: (el, i) => i * 25
|
||||
})
|
||||
// Return to rest gently
|
||||
.add('.part', {
|
||||
translateX: 0,
|
||||
translateY: 0,
|
||||
rotate: 0,
|
||||
scale: 1,
|
||||
opacity: 1,
|
||||
duration: 1500,
|
||||
easing: 'easeInOutQuad',
|
||||
delay: anime.stagger(35, { grid: grid, from: 'center' })
|
||||
});
|
||||
|
||||
staggersAnimation.play();
|
||||
72
demo-v3-balanced.js
Normal file
72
demo-v3-balanced.js
Normal file
@ -0,0 +1,72 @@
|
||||
// Version 3: Balanced - chaotic bursts with organic flow
|
||||
const staggerVisualizerEl = document.querySelector('.stagger-visualizer');
|
||||
const fragment = document.createDocumentFragment();
|
||||
const grid = [12, 6];
|
||||
const col = grid[0];
|
||||
const row = grid[1];
|
||||
const numberOfElements = col * row;
|
||||
|
||||
for (let i = 0; i < numberOfElements; i++) {
|
||||
let div = document.createElement('div');
|
||||
div.className = 'part';
|
||||
div.dataset.index = i;
|
||||
fragment.appendChild(div);
|
||||
}
|
||||
|
||||
staggerVisualizerEl.appendChild(fragment);
|
||||
|
||||
const staggersAnimation = anime.createTimeline({
|
||||
easing: 'easeInOutSine',
|
||||
loop: true,
|
||||
autoplay: false
|
||||
});
|
||||
|
||||
staggersAnimation
|
||||
// Burst outward
|
||||
.add('.part', {
|
||||
translateX: anime.stagger('-.15rem', { grid: grid, from: 'center', axis: 'x' }),
|
||||
translateY: anime.stagger('-.15rem', { grid: grid, from: 'center', axis: 'y' }),
|
||||
scale: 0.5,
|
||||
duration: 300,
|
||||
easing: 'easeOutQuad',
|
||||
delay: anime.stagger(60, { grid: grid, from: 'center' })
|
||||
}, 0)
|
||||
// Spring outward further with randomness
|
||||
.add('.part', {
|
||||
translateX: (el, i) => anime.utils.random(-8, 8),
|
||||
translateY: (el, i) => anime.utils.random(-8, 8),
|
||||
scale: (el) => anime.utils.random(0.7, 1.2),
|
||||
rotate: (el) => anime.utils.random(-90, 90),
|
||||
duration: 600,
|
||||
easing: 'easeOutQuad',
|
||||
delay: anime.stagger(100, { grid: grid, from: 'center' })
|
||||
})
|
||||
// Floating phase - organic motion
|
||||
.add('.part', {
|
||||
translateY: (el, i) => anime.utils.random(-4, 4),
|
||||
rotate: (el, i) => anime.utils.random(-45, 45),
|
||||
duration: 1400,
|
||||
easing: 'easeInOutSine',
|
||||
delay: (el, i) => i * 25 + anime.utils.random(-150, 150)
|
||||
})
|
||||
// Swirl inward
|
||||
.add('.part', {
|
||||
translateX: (el, i) => anime.utils.random(-10, 10),
|
||||
translateY: (el, i) => anime.utils.random(-10, 10),
|
||||
rotate: (el) => anime.utils.random(-180, 180),
|
||||
duration: 800,
|
||||
easing: 'easeInOutQuad',
|
||||
delay: anime.stagger(40, { grid: grid, from: 'center' })
|
||||
})
|
||||
// Converge smoothly
|
||||
.add('.part', {
|
||||
translateX: 0,
|
||||
translateY: 0,
|
||||
rotate: (el) => anime.utils.random(0, 360),
|
||||
scale: 1,
|
||||
duration: 1000,
|
||||
easing: 'easeOutExpo',
|
||||
delay: anime.stagger(50, { grid: grid, from: 'center' })
|
||||
});
|
||||
|
||||
staggersAnimation.play();
|
||||
125
demo-v4-extended-loop.js
Normal file
125
demo-v4-extended-loop.js
Normal file
@ -0,0 +1,125 @@
|
||||
// Version 4: Extended Loop with Smooth Transitions
|
||||
// Creates a much longer animation cycle that loops smoothly without harsh resets
|
||||
const staggerVisualizerEl = document.querySelector('.stagger-visualizer');
|
||||
const fragment = document.createDocumentFragment();
|
||||
const grid = [12, 6];
|
||||
const col = grid[0];
|
||||
const row = grid[1];
|
||||
const numberOfElements = col * row;
|
||||
|
||||
for (let i = 0; i < numberOfElements; i++) {
|
||||
let div = document.createElement('div');
|
||||
div.className = 'part';
|
||||
div.dataset.index = i;
|
||||
fragment.appendChild(div);
|
||||
}
|
||||
|
||||
staggerVisualizerEl.appendChild(fragment);
|
||||
|
||||
// Extended animation cycle - 20 seconds total, much longer so loop is less jarring
|
||||
const staggersAnimation = anime.createTimeline({
|
||||
easing: 'easeInOutSine',
|
||||
loop: true,
|
||||
autoplay: false
|
||||
});
|
||||
|
||||
staggersAnimation
|
||||
// Initial expansion phase
|
||||
.add('.part', {
|
||||
translateX: anime.stagger('-.15rem', { grid: grid, from: 'center', axis: 'x' }),
|
||||
translateY: anime.stagger('-.15rem', { grid: grid, from: 'center', axis: 'y' }),
|
||||
scale: 0.5,
|
||||
duration: 300,
|
||||
easing: 'easeOutQuad',
|
||||
delay: anime.stagger(60, { grid: grid, from: 'center' })
|
||||
}, 0)
|
||||
|
||||
// Chaotic burst phase
|
||||
.add('.part', {
|
||||
translateX: (el, i) => anime.utils.random(-10, 10),
|
||||
translateY: (el, i) => anime.utils.random(-10, 10),
|
||||
scale: (el) => anime.utils.random(0.7, 1.3),
|
||||
rotate: (el) => anime.utils.random(-120, 120),
|
||||
duration: 800,
|
||||
easing: 'easeOutQuad',
|
||||
delay: anime.stagger(80, { grid: grid, from: 'center' })
|
||||
})
|
||||
|
||||
// Floating drift phase 1
|
||||
.add('.part', {
|
||||
translateY: (el, i) => anime.utils.random(-6, 6),
|
||||
rotate: (el) => anime.utils.random(-60, 60),
|
||||
duration: 2000,
|
||||
easing: 'easeInOutSine',
|
||||
delay: (el, i) => i * 20 + anime.utils.random(-200, 200)
|
||||
})
|
||||
|
||||
// Gentle swirl inward
|
||||
.add('.part', {
|
||||
translateX: (el, i) => anime.utils.random(-5, 5),
|
||||
translateY: (el, i) => anime.utils.random(-5, 5),
|
||||
rotate: (el) => anime.utils.random(-45, 45),
|
||||
duration: 1500,
|
||||
easing: 'easeInOutQuad',
|
||||
delay: anime.stagger(50, { grid: grid, from: 'center' })
|
||||
})
|
||||
|
||||
// Floating drift phase 2 - different direction
|
||||
.add('.part', {
|
||||
translateY: (el, i) => anime.utils.random(-8, 8),
|
||||
rotate: (el) => anime.utils.random(-30, 30),
|
||||
duration: 2200,
|
||||
easing: 'easeInOutSine',
|
||||
delay: (el, i) => i * 22 + anime.utils.random(-150, 150)
|
||||
})
|
||||
|
||||
// Slow convergence
|
||||
.add('.part', {
|
||||
translateX: (el) => anime.utils.random(-2, 2),
|
||||
translateY: (el) => anime.utils.random(-2, 2),
|
||||
rotate: (el) => anime.utils.random(-15, 15),
|
||||
duration: 1800,
|
||||
easing: 'easeInOutQuad',
|
||||
delay: anime.stagger(60, { grid: grid, from: 'center' })
|
||||
})
|
||||
|
||||
// Gentle return to center with subtle movement
|
||||
.add('.part', {
|
||||
translateX: (el) => {
|
||||
const index = parseInt(el.dataset.index);
|
||||
return anime.utils.random(-1, 1);
|
||||
},
|
||||
translateY: (el) => {
|
||||
const index = parseInt(el.dataset.index);
|
||||
return anime.utils.random(-1, 1);
|
||||
},
|
||||
rotate: 0,
|
||||
scale: 1,
|
||||
opacity: 1,
|
||||
duration: 2500,
|
||||
easing: 'easeOutQuad',
|
||||
delay: anime.stagger(50, { grid: grid, from: 'center' })
|
||||
})
|
||||
|
||||
// Long rest phase with breathing effect
|
||||
.add('.part', {
|
||||
scale: (el) => anime.utils.random(0.95, 1.05),
|
||||
opacity: (el) => anime.utils.random(0.8, 1),
|
||||
duration: 3000,
|
||||
easing: 'easeInOutSine',
|
||||
delay: (el, i) => i * 15
|
||||
})
|
||||
|
||||
// Return to perfect rest
|
||||
.add('.part', {
|
||||
translateX: 0,
|
||||
translateY: 0,
|
||||
rotate: 0,
|
||||
scale: 1,
|
||||
opacity: 1,
|
||||
duration: 1500,
|
||||
easing: 'easeOutQuad',
|
||||
delay: anime.stagger(40, { grid: grid, from: 'center' })
|
||||
});
|
||||
|
||||
staggersAnimation.play();
|
||||
136
demo-v4-gaussian-noise.js
Normal file
136
demo-v4-gaussian-noise.js
Normal file
@ -0,0 +1,136 @@
|
||||
// Version 4: Gaussian Noise Overlay - Perpetual organic motion
|
||||
const staggerVisualizerEl = document.querySelector('.stagger-visualizer');
|
||||
const fragment = document.createDocumentFragment();
|
||||
const grid = [12, 6];
|
||||
const col = grid[0];
|
||||
const row = grid[1];
|
||||
const numberOfElements = col * row;
|
||||
|
||||
// Store element data for continuous noise animation
|
||||
const elementData = [];
|
||||
|
||||
for (let i = 0; i < numberOfElements; i++) {
|
||||
let div = document.createElement('div');
|
||||
div.className = 'part';
|
||||
div.dataset.index = i;
|
||||
fragment.appendChild(div);
|
||||
|
||||
// Store random animation parameters for each element
|
||||
elementData.push({
|
||||
element: div,
|
||||
index: i,
|
||||
// Random frequencies for sine wave oscillations
|
||||
freqX: anime.utils.random(0.0008, 0.0012),
|
||||
freqY: anime.utils.random(0.0006, 0.0010),
|
||||
freqRot: anime.utils.random(0.0004, 0.0009),
|
||||
freqOpacity: anime.utils.random(0.0005, 0.0008),
|
||||
// Random amplitudes for movement
|
||||
ampX: anime.utils.random(3, 8),
|
||||
ampY: anime.utils.random(3, 8),
|
||||
ampRot: anime.utils.random(10, 25),
|
||||
// Phase offsets so they don't all move together
|
||||
phaseX: anime.utils.random(0, Math.PI * 2),
|
||||
phaseY: anime.utils.random(0, Math.PI * 2),
|
||||
phaseRot: anime.utils.random(0, Math.PI * 2),
|
||||
phaseOpacity: anime.utils.random(0, Math.PI * 2),
|
||||
});
|
||||
}
|
||||
|
||||
staggerVisualizerEl.appendChild(fragment);
|
||||
|
||||
// Initial burst animation
|
||||
const staggersAnimation = anime.createTimeline({
|
||||
easing: 'easeInOutSine',
|
||||
loop: true,
|
||||
autoplay: false
|
||||
});
|
||||
|
||||
staggersAnimation
|
||||
// Burst outward
|
||||
.add('.part', {
|
||||
translateX: anime.stagger('-.15rem', { grid: grid, from: 'center', axis: 'x' }),
|
||||
translateY: anime.stagger('-.15rem', { grid: grid, from: 'center', axis: 'y' }),
|
||||
scale: 0.5,
|
||||
duration: 300,
|
||||
easing: 'easeOutQuad',
|
||||
delay: anime.stagger(60, { grid: grid, from: 'center' })
|
||||
}, 0)
|
||||
// Spring outward further with randomness
|
||||
.add('.part', {
|
||||
translateX: (el, i) => anime.utils.random(-8, 8),
|
||||
translateY: (el, i) => anime.utils.random(-8, 8),
|
||||
scale: (el) => anime.utils.random(0.7, 1.2),
|
||||
rotate: (el) => anime.utils.random(-90, 90),
|
||||
duration: 600,
|
||||
easing: 'easeOutQuad',
|
||||
delay: anime.stagger(100, { grid: grid, from: 'center' })
|
||||
})
|
||||
// Floating phase - long duration
|
||||
.add('.part', {
|
||||
translateY: (el, i) => anime.utils.random(-4, 4),
|
||||
rotate: (el, i) => anime.utils.random(-45, 45),
|
||||
duration: 4000,
|
||||
easing: 'easeInOutSine',
|
||||
delay: (el, i) => i * 25 + anime.utils.random(-150, 150)
|
||||
})
|
||||
// Gentle convergence
|
||||
.add('.part', {
|
||||
translateX: 0,
|
||||
translateY: 0,
|
||||
rotate: (el) => anime.utils.random(0, 360),
|
||||
scale: 1,
|
||||
duration: 2000,
|
||||
easing: 'easeOutQuad',
|
||||
delay: anime.stagger(40, { grid: grid, from: 'center' })
|
||||
})
|
||||
// Rest phase
|
||||
.add('.part', {
|
||||
opacity: 1,
|
||||
duration: 6000,
|
||||
delay: 0
|
||||
}, '-=1000');
|
||||
|
||||
staggersAnimation.play();
|
||||
|
||||
// Continuous Gaussian noise animation - using requestAnimationFrame
|
||||
let startTime = Date.now();
|
||||
|
||||
const applyNoise = () => {
|
||||
const elapsed = Date.now() - startTime;
|
||||
|
||||
elementData.forEach((data) => {
|
||||
// Multi-layered sine waves create Gaussian-like noise
|
||||
const noiseX = Math.sin(elapsed * data.freqX + data.phaseX) * data.ampX +
|
||||
Math.sin(elapsed * data.freqX * 0.3 + data.phaseX) * data.ampX * 0.3;
|
||||
|
||||
const noiseY = Math.sin(elapsed * data.freqY + data.phaseY) * data.ampY +
|
||||
Math.sin(elapsed * data.freqY * 0.4 + data.phaseY) * data.ampY * 0.4;
|
||||
|
||||
const noiseRot = Math.sin(elapsed * data.freqRot + data.phaseRot) * data.ampRot +
|
||||
Math.sin(elapsed * data.freqRot * 0.5 + data.phaseRot) * data.ampRot * 0.2;
|
||||
|
||||
// Smooth opacity breathing
|
||||
const opacityVariation = Math.sin(elapsed * data.freqOpacity + data.phaseOpacity) * 0.12 + 0.88;
|
||||
|
||||
// Apply noise to element
|
||||
data.element.style.filter = `opacity(${opacityVariation})`;
|
||||
|
||||
// Get current computed transform and add noise
|
||||
// This is applied on top of anime's transforms
|
||||
const currentTransform = window.getComputedStyle(data.element).transform;
|
||||
const matrix = currentTransform.match(/matrix.*\((.+)\)/)[1].split(', ');
|
||||
const currentX = parseFloat(matrix[4]) || 0;
|
||||
const currentY = parseFloat(matrix[5]) || 0;
|
||||
|
||||
data.element.style.setProperty('--noise-x', `${noiseX}px`);
|
||||
data.element.style.setProperty('--noise-y', `${noiseY}px`);
|
||||
data.element.style.setProperty('--noise-rot', `${noiseRot}deg`);
|
||||
});
|
||||
|
||||
requestAnimationFrame(applyNoise);
|
||||
};
|
||||
|
||||
// Start noise animation after a brief delay to let anime init
|
||||
setTimeout(() => {
|
||||
applyNoise();
|
||||
}, 100);
|
||||
94
demo-v5-pure-gaussian.js
Normal file
94
demo-v5-pure-gaussian.js
Normal file
@ -0,0 +1,94 @@
|
||||
// Version 5: Pure Gaussian Noise - Infinite organic motion without looping
|
||||
const staggerVisualizerEl = document.querySelector('.stagger-visualizer');
|
||||
const fragment = document.createDocumentFragment();
|
||||
const grid = [12, 6];
|
||||
const col = grid[0];
|
||||
const row = grid[1];
|
||||
const numberOfElements = col * row;
|
||||
|
||||
// Store element data for continuous noise
|
||||
const elementData = [];
|
||||
|
||||
for (let i = 0; i < numberOfElements; i++) {
|
||||
let div = document.createElement('div');
|
||||
div.className = 'part';
|
||||
div.dataset.index = i;
|
||||
fragment.appendChild(div);
|
||||
|
||||
// Each element gets unique noise parameters
|
||||
elementData.push({
|
||||
element: div,
|
||||
index: i,
|
||||
// Multiple frequency layers for natural-looking noise
|
||||
freq1X: anime.utils.random(0.25, 0.75),
|
||||
freq2X: anime.utils.random(0.1, 0.25),
|
||||
freq1Y: anime.utils.random(0.2, 0.6),
|
||||
freq2Y: anime.utils.random(0.05, 0.2),
|
||||
freq1Rot: anime.utils.random(0.1, 0.4),
|
||||
freq2Rot: anime.utils.random(0.025, 0.1),
|
||||
freqScale: anime.utils.random(0.15, 0.4),
|
||||
freqOpacity: anime.utils.random(0.05, 0.25),
|
||||
// Amplitudes
|
||||
ampX: anime.utils.random(4, 12),
|
||||
ampY: anime.utils.random(4, 12),
|
||||
ampRot: anime.utils.random(20, 45),
|
||||
ampScale: anime.utils.random(0.05, 0.15),
|
||||
// Phase offsets for variation
|
||||
phase1X: Math.random() * Math.PI * 2,
|
||||
phase2X: Math.random() * Math.PI * 2,
|
||||
phase1Y: Math.random() * Math.PI * 2,
|
||||
phase2Y: Math.random() * Math.PI * 2,
|
||||
phase1Rot: Math.random() * Math.PI * 2,
|
||||
phase2Rot: Math.random() * Math.PI * 2,
|
||||
phaseScale: Math.random() * Math.PI * 2,
|
||||
phaseOpacity: Math.random() * Math.PI * 2,
|
||||
// Random drift direction preference
|
||||
driftX: anime.utils.random(-0.5, 0.5),
|
||||
driftY: anime.utils.random(-0.5, 0.5),
|
||||
});
|
||||
}
|
||||
|
||||
staggerVisualizerEl.appendChild(fragment);
|
||||
|
||||
// Continuous Gaussian noise animation using requestAnimationFrame
|
||||
let animationStart = Date.now();
|
||||
let lastFrameTime = animationStart;
|
||||
|
||||
const updateNoise = () => {
|
||||
const now = Date.now();
|
||||
const elapsed = (now - animationStart) / 1000; // Convert to seconds
|
||||
lastFrameTime = now;
|
||||
|
||||
elementData.forEach((data) => {
|
||||
// Multi-layered sine waves create smooth Gaussian-like noise
|
||||
// Layer 1: Low frequency, high amplitude (main motion)
|
||||
const noiseX1 = Math.sin(elapsed * data.freq1X + data.phase1X) * data.ampX;
|
||||
const noiseY1 = Math.sin(elapsed * data.freq1Y + data.phase1Y) * data.ampY;
|
||||
const noiseRot1 = Math.sin(elapsed * data.freq1Rot + data.phase1Rot) * data.ampRot;
|
||||
|
||||
// Layer 2: Higher frequency, lower amplitude (detail/jitter)
|
||||
const noiseX2 = Math.sin(elapsed * data.freq2X + data.phase2X) * data.ampX * 0.4;
|
||||
const noiseY2 = Math.sin(elapsed * data.freq2Y + data.phase2Y) * data.ampY * 0.3;
|
||||
const noiseRot2 = Math.sin(elapsed * data.freq2Rot + data.phase2Rot) * data.ampRot * 0.3;
|
||||
|
||||
// Combine layers
|
||||
const totalX = noiseX1 + noiseX2 + (data.driftX * elapsed * 0.01);
|
||||
const totalY = noiseY1 + noiseY2 + (data.driftY * elapsed * 0.01);
|
||||
const totalRot = noiseRot1 + noiseRot2;
|
||||
|
||||
// Scale breathing
|
||||
const scale = 1 + (Math.sin(elapsed * data.freqScale + data.phaseScale) * data.ampScale);
|
||||
|
||||
// Opacity breathing - very subtle
|
||||
const opacity = 0.7 + Math.sin(elapsed * data.freqOpacity + data.phaseOpacity) * 0.25;
|
||||
|
||||
// Apply transforms directly to style
|
||||
data.element.style.transform = `translateX(${totalX}px) translateY(${totalY}px) rotate(${totalRot}deg) scale(${Math.max(0.3, scale)})`;
|
||||
data.element.style.opacity = opacity;
|
||||
});
|
||||
|
||||
requestAnimationFrame(updateNoise);
|
||||
};
|
||||
|
||||
// Start the noise animation
|
||||
updateNoise();
|
||||
39
index.html
39
index.html
@ -7,31 +7,33 @@
|
||||
<title>SolomonLai.ng</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap"
|
||||
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap"
|
||||
rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="stagger-visualizer"></div>
|
||||
<main>
|
||||
<div class="vcard">
|
||||
<div class="vcard-header">
|
||||
<h1 class="vcard-name vcard-header-left"><span class="name-first">Solomon</span></h1>
|
||||
<div class="vcard-header-right">
|
||||
<div class="vcard-header-title">
|
||||
<div class="vcard-header-top">
|
||||
<h1 class="vcard-name"><span class="name-last">Laing</span></h1>
|
||||
<p class="vcard-subtitle">Full Stack Software Developer</p>
|
||||
<button class="theme-toggle" aria-label="Toggle theme">
|
||||
<svg class="icon-moon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" width="18" height="18">
|
||||
<path fill="currentColor"
|
||||
d="M144.7 98.7c-21 34.1-33.1 74.3-33.1 117.3c0 98 62.8 181.4 150.4 211.7c-12.4 2.8-25.3 4.3-38.6 4.3C126 432 48 354 48 256.5C48 175.8 88.6 105.1 150.3 72c-2 8.6-3.6 17.5-5.6 26.7zm27.2-34.3C90 120.2 32 183 32 256.5C32 362.7 117.4 448 223.5 448c42.3 0 81.4-13.6 113.2-36.8c4.2-3 6.2-8.3 5.1-13.4s-5.6-8.8-10.7-9.8C258.4 373.7 200 311.7 200 236.5c0-53 27.8-99.5 69.6-125.8c4.5-2.8 6.9-8 6.1-13.2s-5-9.5-10.2-10.4c-12.1-2-24.5-3.1-37.1-3.1c-19.4 0-38.1 2.8-55.8 7.9l-.7 .2c-.6 .2-1.2 .3-1.7 .5c0 0 0 0 0 0l-.1 0c-.3 .1-.6 .2-1 .2c0 0 0 0 0 0zm0 0" />
|
||||
</svg>
|
||||
<svg class="icon-sun" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="18" height="18">
|
||||
<path fill="currentColor"
|
||||
d="M375.7 19.7c-1.5-8-6.9-14.7-14.4-17.8s-16.1-2.2-22.8 2.4L256 61.1 173.5 4.2c-6.7-4.6-15.3-5.5-22.8-2.4s-12.9 9.8-14.4 17.8l-18.1 98.5L19.7 136.3c-8 1.5-14.7 6.9-17.8 14.4s-2.2 16.1 2.4 22.8L61.1 256 4.2 338.5c-4.6 6.7-5.5 15.3-2.4 22.8s9.8 12.9 17.8 14.4l98.5 18.1 18.1 98.5c1.5 8 6.9 14.7 14.4 17.8s16.1 2.2 22.8-2.4L256 450.9l82.5 56.9c6.7 4.6 15.3 5.5 22.8 2.4s12.9-9.8 14.4-17.8l18.1-98.5 98.5-18.1c8-1.5 14.7-6.9 17.8-14.4s2.2-16.1-2.4-22.8L450.9 256l56.9-82.5c4.6-6.7 5.5-15.3 2.4-22.8s-9.8-12.9-17.8-14.4l-98.5-18.1L375.7 19.7zM269.6 110l65.6-45.2 14.4 78.3c1.8 9.8 9.5 17.5 19.3 19.3l78.3 14.4L402 242.4c-5.7 8.2-5.7 19 0 27.2l45.2 65.6-78.3 14.4c-9.8 1.8-17.5 9.5-19.3 19.3l-14.4 78.3L269.6 402c-8.2-5.7-19-5.7-27.2 0l-65.6 45.2-14.4-78.3c-1.8-9.8-9.5-17.5-19.3-19.3L64.8 335.2 110 269.6c5.7-8.2 5.7-19 0-27.2L64.8 176.8l78.3-14.4c9.8-1.8 17.5-9.5 19.3-19.3l14.4-78.3L242.4 110c8.2 5.7 19 5.7 27.2 0zM256 368a112 112 0 1 0 0-224 112 112 0 1 0 0 224zM192 256a64 64 0 1 1 128 0 64 64 0 1 1 -128 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<button class="theme-toggle" aria-label="Toggle theme">
|
||||
<svg class="icon-moon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" width="20" height="20">
|
||||
<path fill="currentColor"
|
||||
d="M223.5 32C100 32 0 132.3 0 256s100 224 223.5 224c60.6 0 115.5-24.2 155.8-63.4 5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6-96.9 0-175.5-78.8-175.5-176 0-65.8 36-123.1 89.3-153.3 6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z" />
|
||||
</svg>
|
||||
<svg class="icon-sun" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="20" height="20">
|
||||
<path fill="currentColor"
|
||||
d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391l-19.9 107.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM256 160a96 96 0 1 0 0 192 96 96 0 1 0 0-192z" />
|
||||
</svg>
|
||||
</button>
|
||||
<hr class="vcard-divider" />
|
||||
<p class="vcard-subtitle">Full Stack Software Developer</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -88,7 +90,7 @@
|
||||
<path fill="currentColor"
|
||||
d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 288c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128z" />
|
||||
</svg>
|
||||
<a href="assets/resume.pdf" target="_blank">Download Resume <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="12" height="12" style="vertical-align: middle; margin-bottom: 2px;"><path fill="currentColor" d="M320 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l82.7 0L201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L448 109.3l0 82.7c0 17.7 14.3 32 32 32s32-14.3 32-32l0-160c0-17.7-14.3-32-32-32L320 0zM80 32C35.8 32 0 67.8 0 112L0 432c0 44.2 35.8 80 80 80l320 0c44.2 0 80-35.8 80-80l0-160c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 160c0 8.8-7.2 16-16 16L80 448c-8.8 0-16-7.2-16-16l0-320c0-8.8 7.2-16 16-16l160 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L80 32z"/></svg></a>
|
||||
<a href="assets/resume.pdf" target="_blank">Download Resume <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="12" height="12" style="vertical-align: middle; margin-bottom: 2px;"><path fill="currentColor" d="M384 32c17.7 0 32 14.3 32 32l0 160c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-82.7L169.4 323.3c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L306.7 96 224 96c-17.7 0-32-14.3-32-32s14.3-32 32-32l160 0z"/></svg></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -113,9 +115,8 @@
|
||||
<span>GitHub</span>
|
||||
</a>
|
||||
<a class="social-link card" href="https://git.inkletblot.com/inkletblot/" target="_blank">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="5.67 143.05 628.65 387.55" width="20" height="20">
|
||||
<path fill="currentColor" d="M395.9,484.2l-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5,21.2-17.9,33.8-11.8c17.2,8.3,27.1,13,27.1,13l-0.1-109.2l16.7-0.1l0.1,117.1c0,0,57.4,24.2,83.1,40.1c3.7,2.3,10.2,6.8,12.9,14.4c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z"/>
|
||||
<path fill="currentColor" d="M622.7,149.8c-4.1-4.1-9.6-4-9.6-4s-117.2,6.6-177.9,8c-13.3,0.3-26.5,0.6-39.6,0.7c0,39.1,0,78.2,0,117.2c-5.5-2.6-11.1-5.3-16.6-7.9c0-36.4-0.1-109.2-0.1-109.2c-29,0.4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5c-9.8-0.6-22.5-2.1-39,1.5c-8.7,1.8-33.5,7.4-53.8,26.9C-4.9,212.4,6.6,276.2,8,285.8c1.7,11.7,6.9,44.2,31.7,72.5c45.8,56.1,144.4,54.8,144.4,54.8s12.1,28.9,30.6,55.5c25,33.1,50.7,58.9,75.7,62c63,0,188.9-0.1,188.9-0.1s12,0.1,28.3-10.3c14-8.5,26.5-23.4,26.5-23.4s12.9-13.8,30.9-45.3c5.5-9.7,10.1-19.1,14.1-28c0,0,55.2-117.1,55.2-231.1C633.2,157.9,624.7,151.8,622.7,149.8z M125.6,353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6,321.8,60,295.4c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5,38.5-30c13.8-3.7,31-3.1,31-3.1s7.1,59.4,15.7,94.2c7.2,29.2,24.8,77.7,24.8,77.7S142.5,359.9,125.6,353.9z M425.9,461.5c0,0-6.1,14.5-19.6,15.4c-5.8,0.4-10.3-1.2-10.3-1.2s-0.3-0.1-5.3-2.1l-112.9-55c0,0-10.9-5.7-12.8-15.6c-2.2-8.1,2.7-18.1,2.7-18.1L322,273c0,0,4.8-9.7,12.2-13c0.6-0.3,2.3-1,4.5-1.5c8.1-2.1,18,2.8,18,2.8l110.7,53.7c0,0,12.6,5.7,15.3,16.2c1.9,7.4-0.5,14-1.8,17.2C474.6,363.8,425.9,461.5,425.9,461.5z M326.8,380.1c-8.2,0.1-15.4,5.8-17.3,13.8c-1.9,8,2,16.3,9.1,20c7.7,4,17.5,1.8,22.7-5.4c5.1-7.1,4.3-16.9-1.8-23.1l24-49.1c1.5,0.1,3.7,0.2,6.2-0.5c4.1-0.9,7.1-3.6,7.1-3.6c4.2,1.8,8.6,3.8,13.2,6.1c4.8,2.4,9.3,4.9,13.4,7.3c0.9,0.5,1.8,1.1,2.8,1.9c1.6,1.3,3.4,3.1,4.7,5.5c1.9,5.5-1.9,14.9-1.9,14.9c-2.3,7.6-18.4,40.6-18.4,40.6c-8.1-0.2-15.3,5-17.7,12.5c-2.6,8.1,1.1,17.3,8.9,21.3c7.8,4,17.4,1.7,22.5-5.3c5-6.8,4.6-16.3-1.1-22.6c1.9-3.7,3.7-7.4,5.6-11.3c5-10.4,13.5-30.4,13.5-30.4c0.9-1.7,5.7-10.3,2.7-21.3c-2.5-11.4-12.6-16.7-12.6-16.7c-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3c4.7-9.7,9.4-19.3,14.1-29c-4.1-2-8.1-4-12.2-6.1c-4.8,9.8-9.7,19.7-14.5,29.5c-6.7-0.1-12.9,3.5-16.1,9.4c-3.4,6.3-2.7,14.1,1.9,19.8C343.2,346.5,335,363.3,326.8,380.1z"/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">
|
||||
<path fill="currentColor" d="M4.209 4.603c-.247 0-.525.02-.84.088-.333.07-1.28.283-2.054 1.027C-.403 7.25.035 9.685.089 10.052c.065.446.263 1.687 1.21 2.768 1.749 2.141 5.513 2.092 5.513 2.092s.462 1.103 1.168 2.119c.955 1.263 1.936 2.248 2.89 2.367 2.406 0 7.212-.004 7.212-.004s.458.004 1.08-.394c.535-.324 1.013-.893 1.013-.893s.492-.527 1.18-1.73c.21-.37.385-.729.538-1.068 0 0 2.107-4.471 2.107-8.823-.042-1.318-.367-1.55-.443-1.627-.156-.156-.366-.153-.366-.153s-4.475.252-6.792.306c-.508.011-1.012.023-1.512.027v4.474l-.634-.301c0-1.39-.004-4.17-.004-4.17-1.107.016-3.405-.084-3.405-.084s-5.399-.27-5.987-.324c-.187-.011-.401-.032-.648-.032zm.354 1.832h.111s.271 2.269.6 3.597C5.549 11.147 6.22 13 6.22 13s-.996-.119-1.641-.348c-.99-.324-1.409-.714-1.409-.714s-.73-.511-1.096-1.52C1.444 8.73 2.021 7.7 2.021 7.7s.32-.859 1.47-1.145c.395-.106.863-.12 1.072-.12zm8.33 2.554c.26.003.509.127.509.127l.868.422-.529 1.075a.686.686 0 0 0-.614.359.685.685 0 0 0 .072.756l-.939 1.924a.69.69 0 0 0-.66.527.687.687 0 0 0 .347.763.686.686 0 0 0 .867-.206.688.688 0 0 0-.069-.882l.916-1.874a.667.667 0 0 0 .237-.02.657.657 0 0 0 .271-.137 8.826 8.826 0 0 1 1.016.512.761.761 0 0 1 .286.282c.073.21-.073.569-.073.569-.087.29-.702 1.55-.702 1.55a.692.692 0 0 0-.676.477.681.681 0 1 0 1.157-.252c.073-.141.141-.282.214-.431.19-.397.515-1.16.515-1.16.035-.066.218-.394.103-.814-.095-.435-.48-.638-.48-.638-.467-.301-1.116-.58-1.116-.58s0-.156-.042-.27a.688.688 0 0 0-.148-.241l.516-1.062 2.89 1.401s.48.218.583.619c.073.282-.019.534-.069.657-.24.587-2.1 4.317-2.1 4.317s-.232.554-.748.588a1.065 1.065 0 0 1-.393-.045l-.202-.08-4.31-2.1s-.417-.218-.49-.596c-.083-.31.104-.691.104-.691l2.073-4.272s.183-.37.466-.497a.855.855 0 0 1 .35-.077z"/>
|
||||
</svg>
|
||||
<span>Gitea</span>
|
||||
</a>
|
||||
@ -135,13 +136,11 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div class="stagger-visualizer"></div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/animejs@4.0.0/lib/anime.iife.min.js"></script>
|
||||
<script src="theme.js"></script>
|
||||
<script src="main.js"></script>
|
||||
<script src="cards.js"></script>
|
||||
<script src="demo.js"></script>
|
||||
<script src="demo-v5-pure-gaussian.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
113
style.css
113
style.css
@ -2,44 +2,47 @@
|
||||
[data-theme="dark"] {
|
||||
--bg: #17171A;
|
||||
--card-bg: #17171A;
|
||||
--card-border: #CFD1D4;
|
||||
--card-bg-rgb: 23, 23, 26;
|
||||
--card-border: #C9CBCE;
|
||||
--text-primary: #F7F9FC;
|
||||
--text-secondary: #999BA4;
|
||||
--accent-blue: #0083E2;
|
||||
--divider: #CFD1D4;
|
||||
--divider: #C9CBCE;
|
||||
--icon-color: #F7F9FC;
|
||||
--part-color: #F7F9FC;
|
||||
--part-bg-color: rgba(247, 249, 252, 0.06);
|
||||
--card-shadow: none;
|
||||
--stagger-bg: rgba(0, 131, 226, 0.03);
|
||||
--stagger-border: rgba(0, 131, 226, 0.05);
|
||||
}
|
||||
|
||||
[data-theme="light"] {
|
||||
--bg: #F7F9FC;
|
||||
--card-bg: #F7F9FC;
|
||||
--card-bg-rgb: 247, 249, 252;
|
||||
--card-border: #C9CBCE;
|
||||
--text-primary: #393F53;
|
||||
--text-secondary: #6F7588;
|
||||
--accent-blue: #0075CB;
|
||||
--divider: #C9CBCE;
|
||||
--icon-color: #393F53;
|
||||
--part-color: #393F53;
|
||||
--part-bg-color: rgba(57, 63, 83, 0.06);
|
||||
--card-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
|
||||
--stagger-bg: rgba(0, 131, 226, 0.08);
|
||||
--stagger-border: rgba(0, 131, 226, 0.12);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root:not([data-theme="dark"]) {
|
||||
--bg: #F7F9FC;
|
||||
--card-bg: #F7F9FC;
|
||||
--card-bg-rgb: 247, 249, 252;
|
||||
--card-border: #C9CBCE;
|
||||
--text-primary: #393F53;
|
||||
--text-secondary: #6F7588;
|
||||
--accent-blue: #0075CB;
|
||||
--divider: #C9CBCE;
|
||||
--icon-color: #393F53;
|
||||
--part-color: #393F53;
|
||||
--part-bg-color: rgba(57, 63, 83, 0.06);
|
||||
--card-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
|
||||
--stagger-bg: rgba(0, 131, 226, 0.08);
|
||||
--stagger-border: rgba(0, 131, 226, 0.12);
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +60,7 @@ html {
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Roboto, sans-serif;
|
||||
font-family: Montserrat, sans-serif;
|
||||
color: var(--text-primary);
|
||||
background: var(--bg);
|
||||
display: flex;
|
||||
@ -65,6 +68,8 @@ body {
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
padding: 2rem 1rem;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
p,
|
||||
@ -97,19 +102,37 @@ a:visited {
|
||||
transition: color 500ms ease, background-color 500ms ease, border-color 500ms ease, box-shadow 500ms ease, opacity 500ms ease !important;
|
||||
}
|
||||
|
||||
.stagger-visualizer {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
grid-auto-rows: 1fr;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.stagger-visualizer .part {
|
||||
background-color: var(--stagger-bg);
|
||||
border: 1px solid var(--stagger-border);
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
.vcard {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 1000px;
|
||||
width: 100%;
|
||||
border: 1px solid var(--card-border);
|
||||
border-radius: 12px;
|
||||
background: var(--card-bg);
|
||||
box-shadow: var(--card-shadow);
|
||||
background: radial-gradient(circle at center, rgba(var(--card-bg-rgb), 0.95) 0%, rgba(var(--card-bg-rgb), 0.7) 100%);
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.vcard-header {
|
||||
@ -120,15 +143,14 @@ a:visited {
|
||||
.vcard-header-right {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.vcard-header-title {
|
||||
.vcard-header-top {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
gap: 0.25rem;
|
||||
text-align: left;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.vcard-name {
|
||||
@ -148,7 +170,7 @@ a:visited {
|
||||
}
|
||||
|
||||
.vcard-subtitle {
|
||||
font-size: 0.75rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
@ -157,7 +179,7 @@ a:visited {
|
||||
|
||||
.theme-toggle {
|
||||
background: none;
|
||||
border: none;
|
||||
border: 1px solid var(--divider);
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
padding: 6px;
|
||||
@ -191,8 +213,8 @@ a:visited {
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
max-width: 280px;
|
||||
max-width: 100%;
|
||||
aspect-ratio: 3 / 4;
|
||||
border-radius: 10px;
|
||||
object-fit: cover;
|
||||
}
|
||||
@ -200,7 +222,7 @@ a:visited {
|
||||
|
||||
.vcard-content {
|
||||
display: flex;
|
||||
flex-flow: column-reverse;
|
||||
flex-flow: column;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
@ -300,7 +322,9 @@ a:visited {
|
||||
|
||||
svg {
|
||||
flex-shrink: 0;
|
||||
color: var(--icon-color);
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
span {
|
||||
@ -315,24 +339,6 @@ a:visited {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.stagger-visualizer {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 60rem;
|
||||
height: 30rem;
|
||||
top: 0;
|
||||
rotate: 90deg;
|
||||
}
|
||||
|
||||
.part {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
border: 1px solid var(--part-bg-color);
|
||||
background-color: var(--part-bg-color);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.vcard {
|
||||
@ -344,22 +350,19 @@ a:visited {
|
||||
gap: .5rem;
|
||||
}
|
||||
|
||||
.vcard-header-text {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.vcard-body {
|
||||
flex-direction: row;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.vcard-image {
|
||||
flex: 0 0 auto;
|
||||
min-width: 220px;
|
||||
flex: 0 0 35%;
|
||||
max-width: 35%;
|
||||
align-self: stretch;
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
aspect-ratio: auto;
|
||||
object-fit: cover;
|
||||
border-radius: 10px;
|
||||
}
|
||||
@ -380,16 +383,4 @@ a:visited {
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
|
||||
.stagger-visualizer {
|
||||
width: 120rem;
|
||||
height: 60rem;
|
||||
rotate: unset;
|
||||
}
|
||||
|
||||
.part {
|
||||
width: 10rem;
|
||||
height: 10rem;
|
||||
}
|
||||
}
|
||||
|
||||
4
theme.js
4
theme.js
@ -10,8 +10,8 @@ function getPreferredTheme() {
|
||||
|
||||
function applyTheme(theme) {
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
iconMoon.style.display = theme === 'light' ? 'block' : 'none';
|
||||
iconSun.style.display = theme === 'dark' ? 'block' : 'none';
|
||||
iconMoon.style.display = theme === 'dark' ? 'block' : 'none';
|
||||
iconSun.style.display = theme === 'light' ? 'block' : 'none';
|
||||
}
|
||||
|
||||
applyTheme(getPreferredTheme());
|
||||
|
||||
Loading…
Reference in New Issue
Block a user