feat: docs.pivoine.art

This commit is contained in:
2025-10-09 20:15:18 +02:00
parent ae8910aa31
commit 8729e37a66
11 changed files with 1630 additions and 633 deletions

View File

@@ -48,15 +48,17 @@ import { PivoineDocsIcon } from '@/components/icons'
#### Animations
**Default State:**
- Flower starts **closed** as a tight bud (petals 30-50% size)
- Subtle pulsing background
- Twinkling sparkles (2s cycle)
- Orbiting particles (8s cycle)
- Gentle page floating
- Text lines appear on mount
**On Hover:**
**On Hover (Blooming!):**
- Icon scales and lifts
- Petals bloom in sequence (outer → middle → inner)
- Petals **bloom open** to full size (smooth 0.8s transition)
- Bloom sequence: outer → middle → inner (staggered)
- Center glows intensely
- Sparkles burst
- Pages fan out with rotation

View File

@@ -0,0 +1,252 @@
# 🌸 Pivoine Docs Icon - Closed/Open States Update
## ✨ New Behavior: Closed → Open on Hover
The Pivoine Docs Icon now features a beautiful **blooming animation**!
### Initial State: CLOSED 🌷
- Petals start **small and close to the center**
- Outer petals: 30% size, 50% opacity
- Middle petals: 40% size, 60% opacity
- Inner petals: 50% size, 70% opacity
- Flower appears as a **tight bud**
### Hover State: OPEN 🌸
- Petals **bloom outward** to full size
- Smooth 0.8s cubic-bezier transition
- Staggered timing for natural bloom:
- Outer petals bloom first
- Middle petals follow (0.05s delay)
- Inner petals last (0.1s delay)
- All petals reach 100% size and full opacity
- Enhanced glow effects activate
### Visual Effect
```
Closed (default): Hover (blooming): Click (burst):
🌷 → 🌸 → ✨
(bud) (full bloom) (explosion)
```
## 🎯 States Comparison
| State | Outer Petals | Middle Petals | Inner Petals | Effect |
|-------|-------------|---------------|--------------|--------|
| **Closed (default)** | 30% scale | 40% scale | 50% scale | Tight bud |
| **Non-interactive** | 85% scale | 88% scale | 90% scale | Slightly open |
| **Hover (bloom)** | 100% scale | 100% scale | 100% scale | Full bloom |
| **Click (burst)** | 130% scale | 130% scale | 130% scale | Explosion |
## 🎨 Animation Details
### Timing
- **Transition duration**: 0.8s
- **Easing**: cubic-bezier(0.34, 1.56, 0.64, 1) - bouncy bloom
- **Stagger delays**: 0s → 0.05s → 0.1s (outer to inner)
### Visual Changes
**Closed State:**
- Petals scaled down and condensed
- Reduced opacity for depth
- Documents still visible in center
- Sparkles still twinkling
- Subtle animations continue
**Open State (Hover):**
- Petals expand to natural size
- Full opacity and glow
- Enhanced shadow effects
- Center glows brightly
- All animations intensify
## 💡 Why This Works
1. **Metaphor**: Represents knowledge "blooming" as you explore
2. **Engagement**: Invites interaction through visual curiosity
3. **Delight**: Satisfying transformation surprises users
4. **Natural**: Mimics real flower behavior
5. **Smooth**: Staggered timing feels organic
## 📝 Code Changes
### Before (Always Open)
```css
.petal {
transform: scale(1);
opacity: 0.85;
}
```
### After (Closed → Open)
```css
/* Closed by default */
.outer-petal {
transform: scale(0.3);
opacity: 0.5;
}
/* Open on hover */
.is-interactive:hover .outer-petal {
transform: scale(1);
opacity: 0.85;
}
```
## 🎭 Usage Notes
### Interactive Mode (Default)
- Starts **closed** (tight bud)
- **Opens on hover** (full bloom)
- Click for burst effect
- Returns to closed on mouse leave
### Non-Interactive Mode
- Starts **slightly open** (85-90% size)
- Static, no hover effects
- Better for cards/lists where you want visibility
- Still shows the beautiful design
### Example Usage
```tsx
// Interactive - starts closed, blooms on hover
<PivoineDocsIcon size="200px" />
// Non-interactive - slightly open, static
<PivoineDocsIcon size="64px" interactive={false} />
```
## ✨ Benefits
**More engaging** - Invites user interaction
**Metaphorical** - "Knowledge blooms" as you explore
**Delightful** - Surprising transformation creates joy
**Natural** - Follows real flower blooming behavior
**Smooth** - Staggered animation feels organic
**Accessible** - Reduced motion users see semi-open state
## 🎬 Animation Timeline
```
Time Event
────────────────────────────────────
0.0s Mouse enters → bloom starts
0.0s Outer petals begin expanding
0.05s Middle petals begin expanding
0.1s Inner petals begin expanding
0.8s All petals fully bloomed
(continuous hover animations)
──── Mouse leaves → petals close
0.8s Back to closed bud state
```
## 🎨 Visual States
### 1. Initial Load (Closed)
```
• • •
• 🗎 •
• • •
(bud)
```
### 2. Hover Start (Blooming)
```
• •
• • •
• 🗎 •
• • •
• •
(opening)
```
### 3. Fully Bloomed
```
• • •
• • •
• 🗎 •
• • •
• • •
(bloom!)
```
### 4. Click (Burst)
```
• • •
• • •
• ✨🗎✨ •
• • •
• • •
(explode!)
```
## 🔧 Customization
Want to adjust the closed/open states?
### Change Initial Size (Closed State)
```css
.outer-petal {
transform: scale(0.3); /* 0 = fully closed, 1 = fully open */
}
```
### Adjust Bloom Speed
```css
.outer-petal {
transition: all 0.8s; /* Make faster: 0.4s, slower: 1.2s */
}
```
### Modify Stagger Timing
```css
.outer-petal { transition-delay: 0s; }
.middle-petal { transition-delay: 0.05s; } /* Adjust these */
.inner-petal { transition-delay: 0.1s; }
```
## 📱 Responsive Behavior
### Desktop
- Full bloom animation on hover
- Smooth 0.8s transition
- Staggered petal opening
### Mobile/Touch
- Tap to trigger bloom
- Automatic return to closed after interaction
- Optimized touch targets
### Reduced Motion
- Shows in semi-open state (85-90%)
- No animations
- Static display
- Still beautiful and clear
## 🎯 Perfect For
✅ Hero sections - dramatic entrance effect
✅ Landing pages - engaging first impression
✅ About pages - tells a story
✅ Portfolio sites - shows attention to detail
✅ Documentation hubs - "knowledge blooming"
## 🚀 See It In Action
```bash
pnpm dev
```
Visit http://localhost:3000 and:
1. **See the closed bud** on page load
2. **Hover over icon** to watch it bloom open
3. **Move mouse away** to see it close again
4. **Click** for burst effect
5. **Enjoy** the smooth, natural animation!
---
**Updated for Valknar** | [pivoine.art](http://pivoine.art)
*From bud to bloom - knowledge opens up* 🌷→🌸

View File

@@ -0,0 +1,333 @@
/* Kompose Icon Styles */
.kompose-icon-wrapper {
position: relative;
display: inline-block;
cursor: pointer;
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
transform-style: preserve-3d;
}
.kompose-icon-wrapper:not(.is-interactive) {
cursor: default;
}
.kompose-icon {
width: 100%;
height: 100%;
display: block;
filter: drop-shadow(0 4px 20px rgba(0, 220, 130, 0.2));
transition: filter 0.4s ease;
}
/* Hover Effects */
.kompose-icon-wrapper.is-interactive:hover {
transform: scale(1.05) translateY(-2px);
}
.kompose-icon-wrapper.is-interactive:hover .kompose-icon {
filter: drop-shadow(0 8px 30px rgba(0, 220, 130, 0.4));
animation: subtle-pulse 2s ease-in-out infinite;
}
.kompose-icon-wrapper.is-interactive:hover .bg-rect {
animation: bg-glow 2s ease-in-out infinite;
}
.kompose-icon-wrapper.is-interactive:hover .k-letter {
animation: letter-glow 1.5s ease-in-out infinite;
}
.kompose-icon-wrapper.is-interactive:hover .k-vertical {
animation: line-slide-vertical 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.kompose-icon-wrapper.is-interactive:hover .k-diagonal-top {
animation: line-slide-diagonal-top 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) 0.1s;
}
.kompose-icon-wrapper.is-interactive:hover .k-diagonal-bottom {
animation: line-slide-diagonal-bottom 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) 0.2s;
}
.kompose-icon-wrapper.is-interactive:hover .status-dot {
animation: pulse-expand 1s ease-in-out infinite;
}
.kompose-icon-wrapper.is-interactive:hover .status-ring {
animation: ring-pulse 1.5s ease-in-out infinite;
}
.kompose-icon-wrapper.is-interactive:hover .corner {
opacity: 1 !important;
animation: corner-extend 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Click/Active Effects */
.kompose-icon-wrapper.is-clicked {
animation: click-bounce 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.kompose-icon-wrapper.is-clicked .kompose-icon {
animation: rotate-3d 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
filter: drop-shadow(0 12px 40px rgba(0, 220, 130, 0.6));
}
.kompose-icon-wrapper.is-clicked .k-letter {
animation: letter-flash 0.6s ease-out;
filter: url(#intenseglow192);
}
.kompose-icon-wrapper.is-clicked .status-dot {
animation: dot-burst 0.6s ease-out;
}
/* Ripple Effect */
.ripple {
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
border-radius: 50%;
background: radial-gradient(
circle,
rgba(0, 220, 130, 0.6) 0%,
rgba(0, 220, 130, 0) 70%
);
transform: translate(-50%, -50%) scale(0);
animation: ripple-expand 0.8s ease-out;
pointer-events: none;
}
/* Default animations for status dot */
.status-dot {
animation: default-pulse 2s ease-in-out infinite;
}
.status-ring {
animation: default-ring-pulse 2s ease-in-out infinite;
}
/* Keyframe Animations */
@keyframes subtle-pulse {
0%,
100% {
filter: drop-shadow(0 8px 30px rgba(0, 220, 130, 0.4));
}
50% {
filter: drop-shadow(0 8px 35px rgba(0, 220, 130, 0.6));
}
}
@keyframes bg-glow {
0%,
100% {
filter: brightness(1);
}
50% {
filter: brightness(1.1);
}
}
@keyframes letter-glow {
0%,
100% {
filter: url(#glow192);
}
50% {
filter: url(#intenseglow192);
}
}
@keyframes line-slide-vertical {
0% {
stroke-dasharray: 96;
stroke-dashoffset: 96;
}
100% {
stroke-dasharray: 96;
stroke-dashoffset: 0;
}
}
@keyframes line-slide-diagonal-top {
0% {
stroke-dasharray: 68;
stroke-dashoffset: 68;
}
100% {
stroke-dasharray: 68;
stroke-dashoffset: 0;
}
}
@keyframes line-slide-diagonal-bottom {
0% {
stroke-dasharray: 68;
stroke-dashoffset: 68;
}
100% {
stroke-dasharray: 68;
stroke-dashoffset: 0;
}
}
@keyframes pulse-expand {
0%,
100% {
r: 11.52;
opacity: 0.9;
}
50% {
r: 14;
opacity: 1;
}
}
@keyframes ring-pulse {
0%,
100% {
r: 17.28;
opacity: 0.3;
stroke-width: 3;
}
50% {
r: 20;
opacity: 0.6;
stroke-width: 2;
}
}
@keyframes corner-extend {
0% {
stroke-dasharray: 13.44;
stroke-dashoffset: 13.44;
}
100% {
stroke-dasharray: 13.44;
stroke-dashoffset: 0;
}
}
@keyframes click-bounce {
0% {
transform: scale(1) translateY(0) rotateY(0deg);
}
30% {
transform: scale(0.92) translateY(0) rotateY(0deg);
}
50% {
transform: scale(1.08) translateY(-4px) rotateY(180deg);
}
70% {
transform: scale(0.98) translateY(0) rotateY(360deg);
}
100% {
transform: scale(1) translateY(0) rotateY(360deg);
}
}
@keyframes rotate-3d {
0% {
transform: perspective(800px) rotateY(0deg);
}
50% {
transform: perspective(800px) rotateY(180deg);
}
100% {
transform: perspective(800px) rotateY(360deg);
}
}
@keyframes letter-flash {
0%,
100% {
opacity: 1;
}
20%,
60% {
opacity: 0.7;
}
40%,
80% {
opacity: 1;
}
}
@keyframes dot-burst {
0% {
r: 11.52;
opacity: 0.9;
}
50% {
r: 20;
opacity: 1;
}
100% {
r: 11.52;
opacity: 0.9;
}
}
@keyframes ripple-expand {
0% {
transform: translate(-50%, -50%) scale(0);
opacity: 1;
}
100% {
transform: translate(-50%, -50%) scale(2.5);
opacity: 0;
}
}
@keyframes default-pulse {
0%,
100% {
opacity: 0.6;
r: 11.52;
}
50% {
opacity: 1;
r: 13.44;
}
}
@keyframes default-ring-pulse {
0%,
100% {
opacity: 0.3;
}
50% {
opacity: 0.5;
}
}
/* Responsive adjustments */
@media (max-width: 768px) {
.kompose-icon-wrapper.is-interactive:hover {
transform: scale(1.03) translateY(-1px);
}
}
/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
.kompose-icon-wrapper,
.kompose-icon,
.kompose-icon *,
.ripple {
animation: none !important;
transition: none !important;
}
.kompose-icon-wrapper.is-interactive:hover {
transform: scale(1.02);
}
}
/* Touch device optimizations */
@media (hover: none) and (pointer: coarse) {
.kompose-icon-wrapper.is-interactive:active {
transform: scale(0.95);
}
}

View File

@@ -1,6 +1,7 @@
'use client'
import React, { useState } from 'react'
import './KomposeIcon.css'
interface KomposeIconProps {
size?: string
@@ -36,9 +37,16 @@ export default function KomposeIcon({
handleClick()
}
const wrapperClasses = [
'kompose-icon-wrapper',
isClicked && 'is-clicked',
interactive && 'is-interactive',
className
].filter(Boolean).join(' ')
return (
<div
className={`kompose-icon-wrapper ${isClicked ? 'is-clicked' : ''} ${interactive ? 'is-interactive' : ''} ${className}`}
className={wrapperClasses}
onClick={handleClick}
onTouchStart={handleTouch}
style={{ width: size, height: size }}
@@ -106,329 +114,6 @@ export default function KomposeIcon({
{/* Ripple effect container */}
{showRipple && <div className="ripple"></div>}
<style jsx>{`
.kompose-icon-wrapper {
position: relative;
display: inline-block;
cursor: pointer;
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
transform-style: preserve-3d;
}
.kompose-icon-wrapper:not(.is-interactive) {
cursor: default;
}
.kompose-icon {
width: 100%;
height: 100%;
display: block;
filter: drop-shadow(0 4px 20px rgba(0, 220, 130, 0.2));
transition: filter 0.4s ease;
}
/* Hover Effects */
.kompose-icon-wrapper.is-interactive:hover {
transform: scale(1.05) translateY(-2px);
}
.kompose-icon-wrapper.is-interactive:hover .kompose-icon {
filter: drop-shadow(0 8px 30px rgba(0, 220, 130, 0.4));
animation: subtle-pulse 2s ease-in-out infinite;
}
.kompose-icon-wrapper.is-interactive:hover .bg-rect {
fill: url(#bgGrad192);
opacity: 1;
animation: bg-glow 2s ease-in-out infinite;
}
.kompose-icon-wrapper.is-interactive:hover .k-letter {
animation: letter-glow 1.5s ease-in-out infinite;
}
.kompose-icon-wrapper.is-interactive:hover .k-vertical {
animation: line-slide-vertical 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.kompose-icon-wrapper.is-interactive:hover .k-diagonal-top {
animation: line-slide-diagonal-top 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) 0.1s;
}
.kompose-icon-wrapper.is-interactive:hover .k-diagonal-bottom {
animation: line-slide-diagonal-bottom 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) 0.2s;
}
.kompose-icon-wrapper.is-interactive:hover .status-dot {
animation: pulse-expand 1s ease-in-out infinite;
}
.kompose-icon-wrapper.is-interactive:hover .status-ring {
animation: ring-pulse 1.5s ease-in-out infinite;
}
.kompose-icon-wrapper.is-interactive:hover .corner {
opacity: 1 !important;
stroke: #00DC82;
animation: corner-extend 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Click/Active Effects */
.kompose-icon-wrapper.is-clicked {
animation: click-bounce 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.kompose-icon-wrapper.is-clicked .kompose-icon {
animation: rotate-3d 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
filter: drop-shadow(0 12px 40px rgba(0, 220, 130, 0.6));
}
.kompose-icon-wrapper.is-clicked .k-letter {
animation: letter-flash 0.6s ease-out;
filter: url(#intenseglow192);
}
.kompose-icon-wrapper.is-clicked .status-dot {
animation: dot-burst 0.6s ease-out;
}
/* Ripple Effect */
.ripple {
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
border-radius: 50%;
background: radial-gradient(circle, rgba(0, 220, 130, 0.6) 0%, rgba(0, 220, 130, 0) 70%);
transform: translate(-50%, -50%) scale(0);
animation: ripple-expand 0.8s ease-out;
pointer-events: none;
}
/* Default animations for status dot */
.status-dot {
animation: default-pulse 2s ease-in-out infinite;
}
.status-ring {
animation: default-ring-pulse 2s ease-in-out infinite;
}
/* Keyframe Animations */
@keyframes subtle-pulse {
0%, 100% {
filter: drop-shadow(0 8px 30px rgba(0, 220, 130, 0.4));
}
50% {
filter: drop-shadow(0 8px 35px rgba(0, 220, 130, 0.6));
}
}
@keyframes bg-glow {
0%, 100% {
filter: brightness(1);
}
50% {
filter: brightness(1.1);
}
}
@keyframes letter-glow {
0%, 100% {
filter: url(#glow192);
}
50% {
filter: url(#intenseglow192);
}
}
@keyframes line-slide-vertical {
0% {
stroke-dasharray: 96;
stroke-dashoffset: 96;
}
100% {
stroke-dasharray: 96;
stroke-dashoffset: 0;
}
}
@keyframes line-slide-diagonal-top {
0% {
stroke-dasharray: 68;
stroke-dashoffset: 68;
}
100% {
stroke-dasharray: 68;
stroke-dashoffset: 0;
}
}
@keyframes line-slide-diagonal-bottom {
0% {
stroke-dasharray: 68;
stroke-dashoffset: 68;
}
100% {
stroke-dasharray: 68;
stroke-dashoffset: 0;
}
}
@keyframes pulse-expand {
0%, 100% {
r: 11.52;
opacity: 0.9;
}
50% {
r: 14;
opacity: 1;
}
}
@keyframes ring-pulse {
0%, 100% {
r: 17.28;
opacity: 0.3;
stroke-width: 3;
}
50% {
r: 20;
opacity: 0.6;
stroke-width: 2;
}
}
@keyframes corner-extend {
0% {
stroke-dasharray: 13.44;
stroke-dashoffset: 13.44;
}
100% {
stroke-dasharray: 13.44;
stroke-dashoffset: 0;
}
}
@keyframes click-bounce {
0% {
transform: scale(1) translateY(0) rotateY(0deg);
}
30% {
transform: scale(0.92) translateY(0) rotateY(0deg);
}
50% {
transform: scale(1.08) translateY(-4px) rotateY(180deg);
}
70% {
transform: scale(0.98) translateY(0) rotateY(360deg);
}
100% {
transform: scale(1) translateY(0) rotateY(360deg);
}
}
@keyframes rotate-3d {
0% {
transform: perspective(800px) rotateY(0deg);
}
50% {
transform: perspective(800px) rotateY(180deg);
}
100% {
transform: perspective(800px) rotateY(360deg);
}
}
@keyframes letter-flash {
0%, 100% {
opacity: 1;
}
20%, 60% {
opacity: 0.7;
}
40%, 80% {
opacity: 1;
}
}
@keyframes dot-burst {
0% {
r: 11.52;
opacity: 0.9;
}
50% {
r: 20;
opacity: 1;
}
100% {
r: 11.52;
opacity: 0.9;
}
}
@keyframes ripple-expand {
0% {
transform: translate(-50%, -50%) scale(0);
opacity: 1;
}
100% {
transform: translate(-50%, -50%) scale(2.5);
opacity: 0;
}
}
@keyframes default-pulse {
0%, 100% {
opacity: 0.6;
r: 11.52;
}
50% {
opacity: 1;
r: 13.44;
}
}
@keyframes default-ring-pulse {
0%, 100% {
opacity: 0.3;
}
50% {
opacity: 0.5;
}
}
/* Responsive adjustments */
@media (max-width: 768px) {
.kompose-icon-wrapper.is-interactive:hover {
transform: scale(1.03) translateY(-1px);
}
}
/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
.kompose-icon-wrapper,
.kompose-icon,
.kompose-icon *,
.ripple {
animation: none !important;
transition: none !important;
}
.kompose-icon-wrapper.is-interactive:hover {
transform: scale(1.02);
}
}
/* Touch device optimizations */
@media (hover: none) and (pointer: coarse) {
.kompose-icon-wrapper.is-interactive:active {
transform: scale(0.95);
}
}
`}</style>
</div>
)
}

View File

@@ -96,6 +96,10 @@ interface PivoineDocsIconProps {
## ✨ Animations & Effects
### Default State
- **Flower starts CLOSED** as a tight bud 🌷
- Outer petals: 30% size, 50% opacity
- Middle petals: 40% size, 60% opacity
- Inner petals: 50% size, 70% opacity
- **Subtle pulsing** background circle
- **Twinkling sparkles** at corners
- **Orbiting particles** around the flower
@@ -104,11 +108,14 @@ interface PivoineDocsIconProps {
### Hover State (when `interactive={true}`)
- Icon **scales up** and **lifts** (transform 3D)
- **PETALS BLOOM OPEN** to full size! 🌸
- Smooth 0.8s transition
- Staggered timing for natural bloom:
- Outer petals bloom first (0s delay)
- Middle petals follow (0.05s delay)
- Inner petals last (0.1s delay)
- All reach 100% size and full opacity
- Enhanced **shadow and glow** effects
- Petals **bloom** outward in sequence
- Outer petals bloom first
- Middle petals follow (0.1s delay)
- Inner petals last (0.2s delay)
- Center golden circle **glows intensely**
- Sparkles **burst** and expand
- Pages **fan out** slightly with rotation

View File

@@ -0,0 +1,477 @@
/* Pivoine Docs Icon Styles */
.pivoine-docs-icon-wrapper {
position: relative;
display: inline-flex;
flex-direction: column;
align-items: center;
gap: 1rem;
cursor: pointer;
transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
transform-style: preserve-3d;
}
.pivoine-docs-icon-wrapper:not(.is-interactive) {
cursor: default;
}
.pivoine-docs-icon {
width: 100%;
height: 100%;
display: block;
filter: drop-shadow(0 10px 40px rgba(168, 85, 247, 0.3));
transition: filter 0.4s ease;
}
/* Background pulse */
.bg-circle {
animation: bg-pulse 4s ease-in-out infinite;
}
/* CLOSED STATE - Petals start small and close to center */
.outer-petal {
transform-origin: 128px 128px;
transform: scale(0.3);
opacity: 0.5;
transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.middle-petal {
transform-origin: 128px 128px;
transform: scale(0.4);
opacity: 0.6;
transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) 0.05s;
}
.inner-petal {
transform-origin: 128px 128px;
transform: scale(0.5);
opacity: 0.7;
transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) 0.1s;
}
/* Non-interactive version stays slightly more open */
.pivoine-docs-icon-wrapper:not(.is-interactive) .outer-petal {
transform: scale(0.85);
opacity: 0.85;
}
.pivoine-docs-icon-wrapper:not(.is-interactive) .middle-petal {
transform: scale(0.88);
opacity: 0.9;
}
.pivoine-docs-icon-wrapper:not(.is-interactive) .inner-petal {
transform: scale(0.9);
opacity: 0.95;
}
/* Sparkles twinkle */
.sparkle {
animation: twinkle 2s ease-in-out infinite;
}
.sparkle-1 {
animation-delay: 0s;
}
.sparkle-2 {
animation-delay: 0.5s;
}
.sparkle-3 {
animation-delay: 1s;
}
.sparkle-4 {
animation-delay: 1.5s;
}
/* Particles orbit */
.particle {
animation: orbit 8s linear infinite;
transform-origin: 128px 128px;
}
.particle-1 {
animation-delay: 0s;
}
.particle-2 {
animation-delay: 2s;
}
.particle-3 {
animation-delay: 4s;
}
.particle-4 {
animation-delay: 6s;
}
/* Center circle pulse */
.center-circle {
animation: center-pulse 3s ease-in-out infinite;
}
/* Pages subtle movement */
.page {
transform-origin: center;
animation: page-float 3s ease-in-out infinite;
}
.page-1 {
animation-delay: 0s;
}
.page-2 {
animation-delay: 0.3s;
}
.page-3 {
animation-delay: 0.6s;
}
/* Text lines appear */
.text-line {
stroke-dasharray: 30;
stroke-dashoffset: 30;
animation: line-appear 2s ease-out forwards;
}
.line-1 {
animation-delay: 0.2s;
}
.line-2 {
animation-delay: 0.4s;
}
.line-3 {
animation-delay: 0.6s;
}
.line-4 {
animation-delay: 0.8s;
}
.line-5 {
animation-delay: 1s;
}
/* HOVER - BLOOM OPEN! */
.pivoine-docs-icon-wrapper.is-interactive:hover {
transform: scale(1.08) translateY(-8px);
}
.pivoine-docs-icon-wrapper.is-interactive:hover .pivoine-docs-icon {
filter: drop-shadow(0 20px 60px rgba(168, 85, 247, 0.6));
}
/* Petals bloom open on hover */
.pivoine-docs-icon-wrapper.is-interactive:hover .outer-petal {
transform: scale(1);
opacity: 0.85;
filter: url(#intense-glow);
}
.pivoine-docs-icon-wrapper.is-interactive:hover .middle-petal {
transform: scale(1);
opacity: 0.9;
filter: url(#intense-glow);
}
.pivoine-docs-icon-wrapper.is-interactive:hover .inner-petal {
transform: scale(1);
opacity: 0.95;
filter: url(#intense-glow);
}
.pivoine-docs-icon-wrapper.is-interactive:hover .center-circle {
animation: center-glow 1s ease-in-out infinite;
}
.pivoine-docs-icon-wrapper.is-interactive:hover .sparkle {
animation: sparkle-burst 0.8s ease-out infinite;
}
.pivoine-docs-icon-wrapper.is-interactive:hover .page {
animation: page-fan 0.8s ease-out forwards;
}
/* Click effects */
.pivoine-docs-icon-wrapper.is-clicked {
animation: icon-bounce 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.pivoine-docs-icon-wrapper.is-clicked .pivoine-docs-icon {
animation: icon-spin 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
filter: drop-shadow(0 25px 80px rgba(168, 85, 247, 0.9));
}
.pivoine-docs-icon-wrapper.is-clicked .petal {
animation: petal-explode 0.8s ease-out;
}
.pivoine-docs-icon-wrapper.is-clicked .center-circle {
animation: center-burst 0.8s ease-out;
}
/* Ripple effect */
.ripple-effect {
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
border-radius: 50%;
background: radial-gradient(circle, rgba(168, 85, 247, 0.6) 0%, rgba(168, 85, 247, 0) 70%);
transform: translate(-50%, -50%) scale(0);
animation: ripple-expand 1s ease-out;
pointer-events: none;
}
/* Label */
.icon-label {
margin-top: 0.5rem;
}
.label-text {
font-size: 1.25rem;
font-weight: 700;
background: linear-gradient(135deg, #a855f7, #ec4899);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: label-shimmer 3s ease-in-out infinite;
}
/* Keyframes */
@keyframes bg-pulse {
0%,
100% {
opacity: 0.4;
transform: scale(1);
}
50% {
opacity: 0.7;
transform: scale(1.05);
}
}
@keyframes twinkle {
0%,
100% {
opacity: 0.4;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.3);
}
}
@keyframes orbit {
from {
transform: rotate(0deg) translateX(80px) rotate(0deg);
}
to {
transform: rotate(360deg) translateX(80px) rotate(-360deg);
}
}
@keyframes center-pulse {
0%,
100% {
opacity: 0.6;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.15);
}
}
@keyframes page-float {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-2px);
}
}
@keyframes line-appear {
to {
stroke-dashoffset: 0;
}
}
@keyframes center-glow {
0%,
100% {
opacity: 0.8;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.2);
filter: url(#intense-glow);
}
}
@keyframes sparkle-burst {
0%,
100% {
opacity: 0.8;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.8);
}
}
@keyframes page-fan {
0% {
transform: translateY(0) rotate(0deg);
}
100% {
transform: translateY(-3px) rotate(2deg);
}
}
@keyframes icon-bounce {
0% {
transform: scale(1) translateY(0) rotateZ(0deg);
}
30% {
transform: scale(0.9) translateY(0) rotateZ(0deg);
}
60% {
transform: scale(1.15) translateY(-15px) rotateZ(180deg);
}
80% {
transform: scale(0.95) translateY(0) rotateZ(360deg);
}
100% {
transform: scale(1) translateY(0) rotateZ(360deg);
}
}
@keyframes icon-spin {
0% {
transform: perspective(1000px) rotateY(0deg);
}
50% {
transform: perspective(1000px) rotateY(180deg);
}
100% {
transform: perspective(1000px) rotateY(360deg);
}
}
@keyframes petal-explode {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.3);
opacity: 0.8;
filter: url(#intense-glow);
}
100% {
transform: scale(1);
opacity: 1;
filter: url(#petal-glow);
}
}
@keyframes center-burst {
0% {
transform: scale(1);
opacity: 0.8;
}
50% {
transform: scale(1.8);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 0.8;
}
}
@keyframes ripple-expand {
0% {
transform: translate(-50%, -50%) scale(0);
opacity: 1;
}
100% {
transform: translate(-50%, -50%) scale(3);
opacity: 0;
}
}
@keyframes label-shimmer {
0%,
100% {
filter: brightness(1);
}
50% {
filter: brightness(1.3);
}
}
/* Responsive */
@media (max-width: 768px) {
.pivoine-docs-icon-wrapper.is-interactive:hover {
transform: scale(1.05) translateY(-4px);
}
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
.pivoine-docs-icon-wrapper,
.pivoine-docs-icon,
.petal,
.sparkle,
.particle,
.center-circle,
.page,
.text-line,
.ripple-effect,
.label-text {
animation: none !important;
transition: none !important;
}
.pivoine-docs-icon-wrapper.is-interactive:hover {
transform: scale(1.03);
}
/* Show petals in semi-open state for reduced motion */
.outer-petal {
transform: scale(0.85);
opacity: 0.85;
}
.middle-petal {
transform: scale(0.88);
opacity: 0.9;
}
.inner-petal {
transform: scale(0.9);
opacity: 0.95;
}
}
/* Touch devices */
@media (hover: none) and (pointer: coarse) {
.pivoine-docs-icon-wrapper.is-interactive:active {
transform: scale(0.95);
}
}

View File

@@ -1,6 +1,7 @@
'use client'
import React, { useState } from 'react'
import './PivoineDocsIcon.css'
interface PivoineDocsIconProps {
size?: string
@@ -38,9 +39,16 @@ export default function PivoineDocsIcon({
handleClick()
}
const wrapperClasses = [
'pivoine-docs-icon-wrapper',
isClicked && 'is-clicked',
interactive && 'is-interactive',
className
].filter(Boolean).join(' ')
return (
<div
className={`pivoine-docs-icon-wrapper ${isClicked ? 'is-clicked' : ''} ${interactive ? 'is-interactive' : ''} ${className}`}
className={wrapperClasses}
onClick={handleClick}
onTouchStart={handleTouch}
style={{ width: size, height: size }}
@@ -117,7 +125,7 @@ export default function PivoineDocsIcon({
fill={`url(#petal-gradient-${(i % 3) + 1})`}
filter="url(#petal-glow)"
transform={`rotate(${angle} 128 128)`}
opacity="0.85"
style={{ transformOrigin: '128px 128px' }}
/>
))}
</g>
@@ -135,7 +143,7 @@ export default function PivoineDocsIcon({
fill={`url(#petal-gradient-${((i + 1) % 3) + 1})`}
filter="url(#petal-glow)"
transform={`rotate(${angle} 128 128)`}
opacity="0.9"
style={{ transformOrigin: '128px 128px' }}
/>
))}
</g>
@@ -153,14 +161,13 @@ export default function PivoineDocsIcon({
fill={`url(#petal-gradient-${((i + 2) % 3) + 1})`}
filter="url(#petal-glow)"
transform={`rotate(${angle} 128 128)`}
opacity="0.95"
style={{ transformOrigin: '128px 128px' }}
/>
))}
</g>
{/* Center - Document pages */}
<g className="center-docs">
{/* Page stack */}
<rect
className="page page-3"
x="102"
@@ -232,289 +239,6 @@ export default function PivoineDocsIcon({
<span className="label-text">Pivoine Docs</span>
</div>
)}
<style jsx>{`
.pivoine-docs-icon-wrapper {
position: relative;
display: inline-flex;
flex-direction: column;
align-items: center;
gap: 1rem;
cursor: pointer;
transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
transform-style: preserve-3d;
}
.pivoine-docs-icon-wrapper:not(.is-interactive) {
cursor: default;
}
.pivoine-docs-icon {
width: 100%;
height: 100%;
display: block;
filter: drop-shadow(0 10px 40px rgba(168, 85, 247, 0.3));
transition: filter 0.4s ease;
}
/* Background pulse */
.bg-circle {
animation: bg-pulse 4s ease-in-out infinite;
}
/* Petal animations */
.petal {
transform-origin: 128px 128px;
transition: all 0.4s ease;
}
/* Sparkles twinkle */
.sparkle {
animation: twinkle 2s ease-in-out infinite;
}
.sparkle-1 { animation-delay: 0s; }
.sparkle-2 { animation-delay: 0.5s; }
.sparkle-3 { animation-delay: 1s; }
.sparkle-4 { animation-delay: 1.5s; }
/* Particles orbit */
.particle {
animation: orbit 8s linear infinite;
transform-origin: 128px 128px;
}
.particle-1 { animation-delay: 0s; }
.particle-2 { animation-delay: 2s; }
.particle-3 { animation-delay: 4s; }
.particle-4 { animation-delay: 6s; }
/* Center circle pulse */
.center-circle {
animation: center-pulse 3s ease-in-out infinite;
}
/* Pages subtle movement */
.page {
transform-origin: center;
animation: page-float 3s ease-in-out infinite;
}
.page-1 { animation-delay: 0s; }
.page-2 { animation-delay: 0.3s; }
.page-3 { animation-delay: 0.6s; }
/* Text lines appear */
.text-line {
stroke-dasharray: 30;
stroke-dashoffset: 30;
animation: line-appear 2s ease-out forwards;
}
.line-1 { animation-delay: 0.2s; }
.line-2 { animation-delay: 0.4s; }
.line-3 { animation-delay: 0.6s; }
.line-4 { animation-delay: 0.8s; }
.line-5 { animation-delay: 1s; }
/* Hover effects */
.pivoine-docs-icon-wrapper.is-interactive:hover {
transform: scale(1.08) translateY(-8px);
}
.pivoine-docs-icon-wrapper.is-interactive:hover .pivoine-docs-icon {
filter: drop-shadow(0 20px 60px rgba(168, 85, 247, 0.6));
}
.pivoine-docs-icon-wrapper.is-interactive:hover .outer-petal {
animation: petal-bloom 1.2s ease-out forwards;
}
.pivoine-docs-icon-wrapper.is-interactive:hover .middle-petal {
animation: petal-bloom 1.2s ease-out 0.1s forwards;
}
.pivoine-docs-icon-wrapper.is-interactive:hover .inner-petal {
animation: petal-bloom 1.2s ease-out 0.2s forwards;
}
.pivoine-docs-icon-wrapper.is-interactive:hover .center-circle {
animation: center-glow 1s ease-in-out infinite;
}
.pivoine-docs-icon-wrapper.is-interactive:hover .sparkle {
animation: sparkle-burst 0.8s ease-out infinite;
}
.pivoine-docs-icon-wrapper.is-interactive:hover .page {
animation: page-fan 0.8s ease-out forwards;
}
/* Click effects */
.pivoine-docs-icon-wrapper.is-clicked {
animation: icon-bounce 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.pivoine-docs-icon-wrapper.is-clicked .pivoine-docs-icon {
animation: icon-spin 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
filter: drop-shadow(0 25px 80px rgba(168, 85, 247, 0.9));
}
.pivoine-docs-icon-wrapper.is-clicked .petal {
animation: petal-explode 0.8s ease-out;
}
.pivoine-docs-icon-wrapper.is-clicked .center-circle {
animation: center-burst 0.8s ease-out;
}
/* Ripple effect */
.ripple-effect {
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
border-radius: 50%;
background: radial-gradient(circle, rgba(168, 85, 247, 0.6) 0%, rgba(168, 85, 247, 0) 70%);
transform: translate(-50%, -50%) scale(0);
animation: ripple-expand 1s ease-out;
pointer-events: none;
}
/* Label */
.icon-label {
margin-top: 0.5rem;
}
.label-text {
font-size: 1.25rem;
font-weight: 700;
background: linear-gradient(135deg, #a855f7, #ec4899);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: label-shimmer 3s ease-in-out infinite;
}
/* Keyframes */
@keyframes bg-pulse {
0%, 100% { opacity: 0.4; transform: scale(1); }
50% { opacity: 0.7; transform: scale(1.05); }
}
@keyframes twinkle {
0%, 100% { opacity: 0.4; transform: scale(1); }
50% { opacity: 1; transform: scale(1.3); }
}
@keyframes orbit {
from { transform: rotate(0deg) translateX(80px) rotate(0deg); }
to { transform: rotate(360deg) translateX(80px) rotate(-360deg); }
}
@keyframes center-pulse {
0%, 100% { opacity: 0.6; transform: scale(1); }
50% { opacity: 1; transform: scale(1.15); }
}
@keyframes page-float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-2px); }
}
@keyframes line-appear {
to { stroke-dashoffset: 0; }
}
@keyframes petal-bloom {
0% { opacity: 0.85; }
50% { opacity: 1; filter: url(#intense-glow); }
100% { opacity: 0.95; filter: url(#petal-glow); }
}
@keyframes center-glow {
0%, 100% { opacity: 0.8; transform: scale(1); }
50% { opacity: 1; transform: scale(1.2); filter: url(#intense-glow); }
}
@keyframes sparkle-burst {
0%, 100% { opacity: 0.8; transform: scale(1); }
50% { opacity: 1; transform: scale(1.8); }
}
@keyframes page-fan {
0% { transform: translateY(0) rotate(0deg); }
100% { transform: translateY(-3px) rotate(2deg); }
}
@keyframes icon-bounce {
0% { transform: scale(1) translateY(0) rotateZ(0deg); }
30% { transform: scale(0.9) translateY(0) rotateZ(0deg); }
60% { transform: scale(1.15) translateY(-15px) rotateZ(180deg); }
80% { transform: scale(0.95) translateY(0) rotateZ(360deg); }
100% { transform: scale(1) translateY(0) rotateZ(360deg); }
}
@keyframes icon-spin {
0% { transform: perspective(1000px) rotateY(0deg); }
50% { transform: perspective(1000px) rotateY(180deg); }
100% { transform: perspective(1000px) rotateY(360deg); }
}
@keyframes petal-explode {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.3); opacity: 0.8; filter: url(#intense-glow); }
100% { transform: scale(1); opacity: 1; filter: url(#petal-glow); }
}
@keyframes center-burst {
0% { transform: scale(1); opacity: 0.8; }
50% { transform: scale(1.8); opacity: 1; }
100% { transform: scale(1); opacity: 0.8; }
}
@keyframes ripple-expand {
0% { transform: translate(-50%, -50%) scale(0); opacity: 1; }
100% { transform: translate(-50%, -50%) scale(3); opacity: 0; }
}
@keyframes label-shimmer {
0%, 100% { filter: brightness(1); }
50% { filter: brightness(1.3); }
}
/* Responsive */
@media (max-width: 768px) {
.pivoine-docs-icon-wrapper.is-interactive:hover {
transform: scale(1.05) translateY(-4px);
}
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
.pivoine-docs-icon-wrapper,
.pivoine-docs-icon,
.petal,
.sparkle,
.particle,
.center-circle,
.page,
.text-line,
.ripple-effect,
.label-text {
animation: none !important;
transition: none !important;
}
.pivoine-docs-icon-wrapper.is-interactive:hover {
transform: scale(1.03);
}
}
/* Touch devices */
@media (hover: none) and (pointer: coarse) {
.pivoine-docs-icon-wrapper.is-interactive:active {
transform: scale(0.95);
}
}
`}</style>
</div>
)
}