v2.2
MENU ANIMATIONS
UI SURECART
BUTTONS
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ambient Gradient Configurator - BricksFusion</title>
<style>
:root {
--background: #000;
--card-bg: #1e1e1e;
--card-bg-hover: #252525;
--text-primary: #f2f2f7;
--text-secondary: #8e8e93;
--accent: #ef6013;
--accent-hover: #c64c0c;
--border: #2c2c2e;
--shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
--track: #2c2c2e;
--thumb: #ef6013;
--card-radius: 16px;
--input-radius: 8px;
--button-radius: 12px;
--transition: all 0.25s ease;
--font: 'Inter', BlinkMacSystemFont, "San Francisco", "Helvetica Neue", Helvetica, Arial, sans-serif;
--action-bar-height: 70px;
--success: #28a745;
--warning: #ffc107;
--danger: #dc3545;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font);
background-color: var(--background);
color: var(--text-primary);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
padding-bottom: var(--action-bar-height);
}
.action-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: var(--action-bar-height);
background: linear-gradient(145deg, #1a1a1a, #0f0f0f);
border-top: 1px solid var(--border);
z-index: 1000;
display: flex;
align-items: center;
padding: 0 1.5rem;
gap: 1rem;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
}
.breadcrumb {
display: flex;
align-items: center;
gap: 0.5rem;
flex: 1;
}
.breadcrumb-item {
color: var(--text-secondary);
font-size: var(--text-xs);
font-weight: 500;
text-decoration: none;
transition: var(--transition);
padding: 0.5rem 0.75rem;
border-radius: 6px;
}
.breadcrumb-item:hover {
color: var(--text-primary);
background-color: rgba(255, 255, 255, 0.05);
}
.breadcrumb-item.active {
color: var(--accent);
background-color: rgba(239, 96, 19, 0.1);
}
.breadcrumb-separator {
color: var(--text-secondary);
font-size: var(--text-xs);
opacity: 0.5;
}
.action-buttons {
display: flex;
align-items: center;
gap: 0.75rem;
}
.action-btn {
padding: 0.6rem 1rem;
background-color: var(--card-bg);
color: var(--text-primary);
font-family: var(--font);
font-size: var(--text-xs);
font-weight: 500;
border: 1px solid var(--border);
border-radius: var(--button-radius);
cursor: pointer;
transition: var(--transition);
display: flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
white-space: nowrap;
}
.action-btn:hover {
background-color: var(--card-bg-hover);
border-color: var(--accent);
transform: translateY(-1px);
}
.action-btn.primary {
background: linear-gradient(90deg, var(--accent), #ff8c51);
border-color: var(--accent);
color: white;
}
.action-btn.primary:hover {
background: linear-gradient(90deg, var(--accent-hover), #e67a3f);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(239, 96, 19, 0.3);
}
.data-attribute-display {
background-color: rgba(50, 50, 50, 0.8);
border: 1px solid var(--border);
border-radius: 6px;
padding: 0.5rem 0.75rem;
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: var(--text-xs);
color: #ff8c51;
cursor: pointer;
transition: var(--transition);
user-select: all;
}
.data-attribute-display:hover {
background-color: rgba(239, 96, 19, 0.2);
border-color: var(--accent);
}
.container {
max-width: 100%;
margin: 0 auto;
padding: 2rem 1.5rem;
}
.page-header {
text-align: center;
margin-bottom: 2rem;
}
.page-title {
font-size: 2.5rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 0.5rem;
background: linear-gradient(90deg, var(--accent), #ff8c51);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.page-subtitle {
font-size: var(--text-s);
color: var(--text-secondary);
font-weight: 500;
}
.instructions-toggle {
margin-bottom: 2rem;
}
.instructions-card {
background-color: var(--card-bg);
border: 1px solid var(--border);
border-radius: var(--card-radius);
box-shadow: var(--shadow);
overflow: hidden;
transition: var(--transition);
}
.instructions-header {
padding: 1rem 1.5rem;
cursor: pointer;
transition: var(--transition);
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid transparent;
}
.instructions-header:hover {
background-color: var(--card-bg-hover);
}
.instructions-card.expanded .instructions-header {
border-bottom-color: var(--border);
}
.instructions-title {
font-size: var(--text-s);
font-weight: 600;
}
.toggle-icon {
font-size: 1.2em;
transition: transform 0.3s ease;
}
.toggle-icon.expanded {
transform: rotate(180deg);
}
.instructions-content {
padding: 0 1.5rem;
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease, padding 0.3s ease;
}
.instructions-content.show {
max-height: 500px;
padding: 1.5rem;
}
.instructions-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
}
.how-to-use ol {
padding-left: 1.5rem;
}
.how-to-use li {
margin-bottom: 0.75rem;
font-size: var(--text-xs);
color: var(--text-secondary);
line-height: 1.6;
}
.how-to-use strong {
color: var(--text-primary);
font-weight: 600;
}
.how-to-use code {
background-color: rgba(50, 50, 50, 0.5);
padding: 0.2rem 0.5rem;
border-radius: 4px;
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: var(--text-xs);
color: #ff8c51;
}
.content {
display: grid;
grid-template-columns: 1fr 500px;
gap: 2rem;
align-items: start;
}
.preview-section {
position: sticky;
top: 2rem;
}
.controls-section {
max-width: 500px;
}
.card {
background-color: var(--card-bg);
border-radius: var(--card-radius);
box-shadow: var(--shadow);
overflow: hidden;
margin-bottom: 1.5rem;
border: 1px solid var(--border);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
}
.preview-container {
height: 400px;
width: 100%;
position: relative;
overflow: hidden;
border-radius: var(--card-radius);
background-color: #000000;
border: 1px solid var(--border);
box-shadow: var(--shadow);
}
.preview-content {
color: white;
text-align: center;
font-weight: bold;
font-size: var(--text-s);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
}
.preview-controls {
position: absolute;
top: 1rem;
right: 1rem;
display: flex;
gap: 0.5rem;
z-index: 10;
}
.preview-btn {
padding: 0.5rem;
background-color: rgba(0, 0, 0, 0.7);
color: white;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
cursor: pointer;
transition: var(--transition);
font-size: var(--text-xs);
backdrop-filter: blur(5px);
}
.preview-btn:hover {
background-color: var(--accent);
border-color: var(--accent);
}
.card-heading {
padding: 1rem 1.5rem;
font-size: var(--text-s);
font-weight: 600;
border-bottom: 1px solid var(--border);
letter-spacing: 0.3px;
display: flex;
justify-content: space-between;
align-items: center;
}
.card-actions {
display: flex;
gap: 0.5rem;
}
.card-action-btn {
padding: 0.4rem 0.8rem;
background-color: transparent;
color: var(--text-secondary);
border: 1px solid var(--border);
border-radius: 6px;
cursor: pointer;
font-size: var(--text-xs);
transition: var(--transition);
}
.card-action-btn:hover {
color: var(--text-primary);
border-color: var(--accent);
background-color: rgba(239, 96, 19, 0.1);
}
.card-content {
padding: 1.5rem;
}
.control-group {
margin-bottom: 1.5rem;
position: relative;
}
.control-group:last-child {
margin-bottom: 0;
}
.control-label {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.75rem;
}
.label-text {
font-size: var(--text-xs);
font-weight: 500;
letter-spacing: 0.2px;
display: flex;
align-items: center;
gap: 0.5rem;
}
.help-tooltip {
cursor: help;
opacity: 0.7;
transition: var(--transition);
}
.help-tooltip:hover {
opacity: 1;
color: var(--accent);
}
.value-display {
display: flex;
align-items: center;
gap: 0.5rem;
}
.value-text {
font-size: var(--text-xs);
color: var(--text-secondary);
background-color: rgba(50, 50, 50, 0.5);
padding: 2px 8px;
border-radius: 4px;
min-width: 45px;
text-align: center;
}
.reset-btn {
padding: 0.2rem 0.4rem;
background-color: transparent;
color: var(--text-secondary);
border: 1px solid var(--border);
border-radius: 4px;
cursor: pointer;
font-size: 10px;
transition: var(--transition);
}
.reset-btn:hover {
color: var(--danger);
border-color: var(--danger);
background-color: rgba(220, 53, 69, 0.1);
}
input[type="range"] {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 6px;
background: var(--track);
border-radius: 3px;
outline: none;
margin: 0.8rem 0;
position: relative;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
background: var(--thumb);
border-radius: 50%;
cursor: pointer;
transition: var(--transition);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
input[type="range"]::-webkit-slider-thumb:hover {
transform: scale(1.2);
box-shadow: 0 0 10px rgba(239, 96, 19, 0.5);
}
.color-list {
display: flex;
flex-direction: column;
gap: 1rem;
margin-bottom: 1.5rem;
}
.color-row {
display: flex;
align-items: center;
gap: 1.25rem;
padding: 1rem 1.25rem;
background-color: rgba(30, 30, 30, 0.7);
border: 1px solid var(--border);
border-radius: var(--input-radius);
transition: var(--transition);
}
.color-row:hover {
border-color: var(--accent);
box-shadow: 0 0 0 1px rgba(239, 96, 19, 0.1);
}
/* Color picker styling like Aura Flow */
.color-picker-container {
position: relative;
width: 40px;
height: 40px;
border-radius: 8px;
overflow: hidden;
border: 2px solid var(--border);
cursor: pointer;
transition: var(--transition);
flex-shrink: 0;
background: linear-gradient(45deg, #f0f0f0 25%, transparent 25%),
linear-gradient(-45deg, #f0f0f0 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #f0f0f0 75%),
linear-gradient(-45deg, transparent 75%, #f0f0f0 75%);
background-size: 8px 8px;
background-position: 0 0, 0 4px, 4px -4px, -4px 0px;
}
.color-picker-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--current-color, #ee7752);
border-radius: 6px;
z-index: 1;
}
.color-picker-container:hover {
border-color: var(--accent);
transform: scale(1.05);
}
input[type="color"] {
position: absolute;
top: -2px;
left: -2px;
width: calc(100% + 4px);
height: calc(100% + 4px);
border: none;
cursor: pointer;
background: transparent;
z-index: 2;
opacity: 0;
}
.color-input-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.color-label {
font-size: 10px;
font-weight: 500;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-left: 0.25rem;
}
.color-input {
padding: 0.5rem 0.75rem;
background-color: rgba(0, 0, 0, 0.3);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text-primary);
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: 12px;
transition: var(--transition);
min-width: 0;
}
.color-input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 1px rgba(239, 96, 19, 0.2);
outline: none;
}
.color-input.invalid {
border-color: var(--danger);
box-shadow: 0 0 0 1px rgba(220, 53, 69, 0.2);
}
.hex-input,
.hsl-input {
width: 100%;
}
.color-input-group:nth-child(2) {
flex: 0.3;
}
.color-input-group:nth-child(3) {
flex: 0.7;
}
.switch-container {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.2rem;
padding: 0.5rem 0;
}
.switch-label {
font-size: var(--text-xs);
font-weight: 500;
letter-spacing: 0.2px;
display: flex;
align-items: center;
gap: 0.5rem;
}
.switch {
position: relative;
display: inline-block;
width: 52px;
height: 28px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--track);
transition: var(--transition);
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 20px;
width: 20px;
left: 4px;
bottom: 4px;
background-color: #f2f2f7;
transition: var(--transition);
border-radius: 50%;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
input:checked + .slider {
background-color: var(--accent);
}
input:checked + .slider:before {
transform: translateX(24px);
}
.quick-actions {
display: flex;
gap: 0.5rem;
margin-bottom: 1.5rem;
}
.quick-action-btn {
flex: 1;
padding: 0.6rem;
background-color: rgba(30, 30, 30, 0.7);
border: 1px solid var(--border);
border-radius: var(--input-radius);
color: var(--text-secondary);
cursor: pointer;
transition: var(--transition);
font-size: var(--text-xs);
text-align: center;
}
.quick-action-btn:hover {
background-color: var(--accent);
color: white;
border-color: var(--accent);
}
.notification {
position: fixed;
bottom: calc(var(--action-bar-height) + 1rem);
left: 50%;
background-color: var(--success);
color: white;
padding: 0.75rem 1rem;
border-radius: var(--input-radius);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
z-index: 1001;
transform: translate(-50%, 200px);
opacity: 0;
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.4s ease;
font-size: var(--text-xs);
font-weight: 500;
max-width: 320px;
word-wrap: break-word;
line-height: 1.4;
text-align: center;
}
.notification.show {
transform: translate(-50%, 0);
opacity: 1;
}
@media (max-width: 1200px) {
.content {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.preview-section {
position: static;
}
.controls-section {
max-width: 100%;
}
}
@media (max-width: 768px) {
.action-bar {
flex-direction: column;
height: auto;
min-height: var(--action-bar-height);
padding: 0.75rem;
}
.breadcrumb {
order: 1;
width: 100%;
}
.action-buttons {
order: 2;
width: 100%;
justify-content: center;
flex-wrap: wrap;
}
body {
padding-bottom: calc(var(--action-bar-height) + 20px);
}
.notification {
bottom: calc(var(--action-bar-height) + 2rem);
max-width: 280px;
transform: translate(-50%, 250px);
}
.notification.show {
transform: translate(-50%, 0);
opacity: 1;
}
.color-row {
flex-direction: column;
align-items: stretch;
gap: 1rem;
padding: 1rem;
}
.color-picker-container {
align-self: center;
margin-bottom: 0.5rem;
}
.color-input-group {
align-items: stretch;
}
.hex-input,
.hsl-input {
width: 100%;
}
.preview-container {
height: 300px;
}
.data-attribute-display {
font-size: 10px;
padding: 0.4rem 0.6rem;
}
.action-btn {
font-size: 11px;
padding: 0.5rem 0.8rem;
}
.page-title {
font-size: 2rem;
}
}
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
button:focus-visible,
input:focus-visible,
.action-btn:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--background);
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--accent);
}
.loading {
opacity: 0.6;
pointer-events: none;
position: relative;
}
.loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
margin: -10px 0 0 -10px;
border: 2px solid var(--border);
border-top-color: var(--accent);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="action-bar">
<nav class="breadcrumb">
<a href="https://bricksfusion.com" class="breadcrumb-item">Home</a>
<span class="breadcrumb-separator">›</span>
<a href="https://bricksfusion.com/corebackground/" class="breadcrumb-item">Core Backgrounds</a>
<span class="breadcrumb-separator">›</span>
<span class="breadcrumb-item active">Ambient Gradient</span>
</nav>
<div class="action-buttons">
<div class="data-attribute-display" id="quick-attribute" title="Click to copy data attribute">
data-ambient-gradient
</div>
<button class="action-btn primary" id="download-config" title="Copy JavaScript code (Ctrl+D)" data-protection-animation="true">
<span>📋</span>
Copy JS
</button>
<button class="action-btn" id="copy-full-section" title="Copy complete section JSON for Bricks Builder (Ctrl+S)" data-protection-animation="true">
<span>📦</span>
Copy Full Section
</button>
</div>
</div>
<div class="container">
<div class="page-header">
<h1 class="page-title">Ambient Gradient</h1>
<p class="page-subtitle">Interactive gradient backgrounds for Bricks Builder</p>
</div>
<div class="instructions-toggle">
<div class="instructions-card" id="instructions-card">
<div class="instructions-header" id="instructions-toggle">
<div class="instructions-title">
How to Use & Code Information
</div>
<span class="toggle-icon">▼</span>
</div>
<div class="instructions-content" id="instructions-content">
<div class="instructions-grid">
<div class="how-to-use">
<ol>
<li>Customize your gradient using the controls below</li>
<li>Click <strong>Copy JS</strong> to copy the JavaScript code to clipboard</li>
<li>In Bricks Builder, add a <strong>Code</strong> element</li>
<li>Paste or upload the JavaScript code</li>
<li>To add the effect to any section: go to <strong>Section → Style → Attributes</strong>, add <code>data-ambient-gradient</code> as attribute name (leave value empty)</li>
</ol>
</div>
</div>
</div>
</div>
</div>
<div class="content">
<section class="preview-section">
<div class="preview-container" id="gradient-preview" data-ambient-gradient="true">
<div class="preview-content">Interactive Gradient Preview</div>
<div class="preview-controls">
<button class="preview-btn" id="randomize-gradient" title="Randomize (R)">🎲</button>
</div>
</div>
</section>
<section class="controls-section">
<div class="card">
<div class="card-heading">
Gradient Colors
<div class="card-actions">
<button class="card-action-btn" id="reset-colors" title="Reset Colors">↺</button>
</div>
</div>
<div class="card-content">
<div class="color-list">
<div class="color-row">
<div class="color-picker-container">
<input type="color" id="color1" value="#ee7752">
</div>
<div class="color-input-group">
<span class="color-label">HEX</span>
<input type="text" class="color-input hex-input" id="color1-hex" value="#ee7752" placeholder="#FFFFFF">
</div>
<div class="color-input-group">
<span class="color-label">HSL</span>
<input type="text" class="color-input hsl-input" id="color1-hsl" placeholder="hsl(0, 100%, 50%)">
</div>
</div>
<div class="color-row">
<div class="color-picker-container">
<input type="color" id="color2" value="#e73c7e">
</div>
<div class="color-input-group">
<span class="color-label">HEX</span>
<input type="text" class="color-input hex-input" id="color2-hex" value="#e73c7e" placeholder="#FFFFFF">
</div>
<div class="color-input-group">
<span class="color-label">HSL</span>
<input type="text" class="color-input hsl-input" id="color2-hsl" placeholder="hsl(0, 100%, 50%)">
</div>
</div>
<div class="color-row">
<div class="color-picker-container">
<input type="color" id="color3" value="#23a6d5">
</div>
<div class="color-input-group">
<span class="color-label">HEX</span>
<input type="text" class="color-input hex-input" id="color3-hex" value="#23a6d5" placeholder="#FFFFFF">
</div>
<div class="color-input-group">
<span class="color-label">HSL</span>
<input type="text" class="color-input hsl-input" id="color3-hsl" placeholder="hsl(0, 100%, 50%)">
</div>
</div>
<div class="color-row">
<div class="color-picker-container">
<input type="color" id="color4" value="#23d5ab">
</div>
<div class="color-input-group">
<span class="color-label">HEX</span>
<input type="text" class="color-input hex-input" id="color4-hex" value="#23d5ab" placeholder="#FFFFFF">
</div>
<div class="color-input-group">
<span class="color-label">HSL</span>
<input type="text" class="color-input hsl-input" id="color4-hsl" placeholder="hsl(0, 100%, 50%)">
</div>
</div>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Gradient Angle
<span class="help-tooltip" title="Controls the direction of the gradient">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="angle-value">45</span>°</span>
<button class="reset-btn" onclick="resetParameter('gradient-angle', 45)">↺</button>
</div>
</div>
<input type="range" id="gradient-angle" min="0" max="360" value="45">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Background Size
<span class="help-tooltip" title="Larger values create more dramatic effects">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="bg-size-value">400</span>%</span>
<button class="reset-btn" onclick="resetParameter('bg-size', 400)">↺</button>
</div>
</div>
<input type="range" id="bg-size" min="100" max="800" value="400" step="50">
</div>
</div>
</div>
<div class="card">
<div class="card-heading">
Animation Settings
<div class="card-actions">
<button class="card-action-btn" id="reset-animation" title="Reset Animation Settings">↺</button>
</div>
</div>
<div class="card-content">
<div class="switch-container">
<span class="switch-label">
Auto Animation
<span class="help-tooltip" title="Enable automatic movement without mouse interaction">ℹ</span>
</span>
<label class="switch">
<input type="checkbox" id="auto-animate">
<span class="slider"></span>
</label>
</div>
<div id="auto-animation-settings">
<div class="control-group">
<div class="control-label">
<span class="label-text">Animation Speed</span>
<div class="value-display">
<span class="value-text" id="auto-speed-value">0.005</span>
<button class="reset-btn" onclick="resetParameter('auto-speed', 0.005)">↺</button>
</div>
</div>
<input type="range" id="auto-speed" min="0.001" max="0.01" step="0.001" value="0.005">
</div>
</div>
<div id="mouse-animation-settings">
<div class="control-group">
<div class="control-label">
<span class="label-text">Mouse Influence</span>
<div class="value-display">
<span class="value-text" id="mouse-influence-value">100</span>%</span>
<button class="reset-btn" onclick="resetParameter('mouse-influence', 100)">↺</button>
</div>
</div>
<input type="range" id="mouse-influence" min="0" max="100" value="100">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Idle Animation Speed</span>
<div class="value-display">
<span class="value-text" id="idle-speed-value">0.05</span>
<button class="reset-btn" onclick="resetParameter('idle-speed', 0.05)">↺</button>
</div>
</div>
<input type="range" id="idle-speed" min="0.01" max="0.2" step="0.01" value="0.05">
</div>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Transition Smoothness</span>
<div class="value-display">
<span class="value-text" id="smoothness-value">0.5</span>s</span>
<button class="reset-btn" onclick="resetParameter('transition-smoothness', 0.5)">↺</button>
</div>
</div>
<input type="range" id="transition-smoothness" min="0" max="2" step="0.1" value="0.5">
</div>
</div>
</div>
<div class="card">
<div class="card-heading">
Advanced Options
<div class="card-actions">
<button class="card-action-btn" id="reset-advanced" title="Reset Advanced Settings">↺</button>
</div>
</div>
<div class="card-content">
<div class="control-group">
<div class="control-label">
<span class="label-text">Z-Index Position</span>
<div class="value-display">
<span class="value-text" id="z-index-value">1</span>
<button class="reset-btn" onclick="resetParameter('z-index', 1)">↺</button>
</div>
</div>
<input type="range" id="z-index" min="-10" max="10" value="1" step="1">
</div>
<div class="switch-container">
<span class="switch-label">Keep Content Centered</span>
<label class="switch">
<input type="checkbox" id="center-content" checked>
<span class="slider"></span>
</label>
</div>
<div class="switch-container">
<span class="switch-label">Border Radius Inherit</span>
<label class="switch">
<input type="checkbox" id="border-radius-inherit" checked>
<span class="slider"></span>
</label>
</div>
</div>
</div>
</section>
</div>
</div>
<div class="notification" id="notification"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
let gradientConfig = {
colors: ['#ee7752', '#e73c7e', '#23a6d5', '#23d5ab'],
angle: 45,
backgroundSize: 400,
autoAnimate: false,
autoSpeed: 0.005,
mouseInfluence: 100,
idleSpeed: 0.05,
transitionSmoothness: 0.5,
zIndex: 1,
centerContent: true,
borderRadiusInherit: true
};
const defaultConfig = { ...gradientConfig };
let activeGradient = null;
function initAmbientGradient() {
const sections = document.querySelectorAll('[data-ambient-gradient]:not([data-ambient-initialized="true"])');
sections.forEach((section) => {
const isAutoAnimate = section.hasAttribute('data-ambient-gradient-auto') || gradientConfig.autoAnimate;
let colors = [];
for (let i = 1; i <= 4; i++) {
const colorAttr = `data-ambient-gradient-color${i}`;
if (section.hasAttribute(colorAttr)) {
colors.push(section.getAttribute(colorAttr));
}
}
if (colors.length === 0) {
colors = gradientConfig.colors;
}
const angle = section.hasAttribute('data-ambient-gradient-angle')
? parseFloat(section.getAttribute('data-ambient-gradient-angle'))
: gradientConfig.angle;
const bgSize = section.hasAttribute('data-ambient-gradient-size')
? parseFloat(section.getAttribute('data-ambient-gradient-size'))
: gradientConfig.backgroundSize;
const autoSpeed = section.hasAttribute('data-ambient-gradient-auto-speed')
? parseFloat(section.getAttribute('data-ambient-gradient-auto-speed'))
: gradientConfig.autoSpeed;
const mouseInfluence = section.hasAttribute('data-ambient-gradient-mouse-influence')
? parseFloat(section.getAttribute('data-ambient-gradient-mouse-influence'))
: gradientConfig.mouseInfluence / 100;
const idleSpeed = section.hasAttribute('data-ambient-gradient-idle-speed')
? parseFloat(section.getAttribute('data-ambient-gradient-idle-speed'))
: gradientConfig.idleSpeed;
const transitionSmoothness = section.hasAttribute('data-ambient-gradient-transition')
? parseFloat(section.getAttribute('data-ambient-gradient-transition'))
: gradientConfig.transitionSmoothness;
const zIndex = section.hasAttribute('data-ambient-gradient-z-index')
? parseInt(section.getAttribute('data-ambient-gradient-z-index'))
: gradientConfig.zIndex;
const centerContent = section.hasAttribute('data-ambient-gradient-center')
? section.getAttribute('data-ambient-gradient-center') === 'true'
: gradientConfig.centerContent;
const borderRadiusInherit = section.hasAttribute('data-ambient-gradient-border-inherit')
? section.getAttribute('data-ambient-gradient-border-inherit') === 'true'
: gradientConfig.borderRadiusInherit;
const options = {
colors,
angle,
backgroundSize: bgSize,
autoAnimate: isAutoAnimate,
autoSpeed,
mouseInfluence,
idleSpeed,
transitionSmoothness,
zIndex,
centerContent,
borderRadiusInherit
};
const state = {
mouseX: 0.5,
mouseY: 0.5,
targetX: 0.5,
targetY: 0.5,
isMouseInside: false,
isAutoAnimate: isAutoAnimate
};
setupGradient(section, options);
if (!isAutoAnimate) {
setupEventListeners(section, state, options);
}
startAnimation(section, state, options);
section.dataset.ambientInitialized = 'true';
if (section.id === 'gradient-preview') {
activeGradient = { element: section, state, options };
gradientConfig = {
colors: options.colors,
angle: options.angle,
backgroundSize: options.backgroundSize,
autoAnimate: options.autoAnimate,
autoSpeed: options.autoSpeed,
mouseInfluence: options.mouseInfluence * 100,
idleSpeed: options.idleSpeed,
transitionSmoothness: options.transitionSmoothness,
zIndex: options.zIndex,
centerContent: options.centerContent,
borderRadiusInherit: options.borderRadiusInherit
};
}
});
}
function setupGradient(element, options) {
const gradientElement = document.createElement('div');
gradientElement.style.position = 'absolute';
gradientElement.style.top = '0';
gradientElement.style.left = '0';
gradientElement.style.width = '100%';
gradientElement.style.height = '100%';
gradientElement.style.background = 'linear-gradient(' + options.angle + 'deg, ' + options.colors.join(', ') + ')';
gradientElement.style.backgroundSize = options.backgroundSize + '% ' + options.backgroundSize + '%';
gradientElement.style.transition = 'background-position ' + options.transitionSmoothness + 's ease-out';
gradientElement.style.zIndex = options.zIndex;
if (options.borderRadiusInherit) {
gradientElement.style.borderRadius = 'inherit';
}
const computedStyle = window.getComputedStyle(element);
if (computedStyle.position === 'static') {
element.style.position = 'relative';
}
if (element.firstChild) {
element.insertBefore(gradientElement, element.firstChild);
} else {
element.appendChild(gradientElement);
}
element.gradientElement = gradientElement;
if (options.centerContent) {
Array.from(element.children).forEach(child => {
if (child !== gradientElement) {
if (getComputedStyle(child).position === 'static') {
child.style.position = 'relative';
}
child.style.zIndex = '1';
}
});
}
}
function setupEventListeners(element, state, options) {
element.addEventListener('mousemove', (e) => {
const rect = element.getBoundingClientRect();
state.mouseX = (e.clientX - rect.left) / rect.width;
state.mouseY = (e.clientY - rect.top) / rect.height;
state.isMouseInside = true;
});
element.addEventListener('mouseleave', () => {
state.isMouseInside = false;
state.targetX = Math.random();
state.targetY = Math.random();
});
element.addEventListener('mouseenter', () => {
state.isMouseInside = true;
});
element.addEventListener('touchmove', (e) => {
e.preventDefault();
const touch = e.touches[0];
const rect = element.getBoundingClientRect();
state.mouseX = (touch.clientX - rect.left) / rect.width;
state.mouseY = (touch.clientY - rect.top) / rect.height;
state.isMouseInside = true;
}, { passive: false });
element.addEventListener('touchend', () => {
state.isMouseInside = false;
state.targetX = Math.random();
state.targetY = Math.random();
});
element.addEventListener('focus', () => {
state.isMouseInside = true;
});
element.addEventListener('blur', () => {
state.isMouseInside = false;
state.targetX = Math.random();
state.targetY = Math.random();
});
}
function startAnimation(element, state, options) {
let animationFrameId;
let autoAnimateTime = 0;
function updateGradient() {
if (state.isAutoAnimate) {
autoAnimateTime += options.autoSpeed;
state.mouseX = (Math.sin(autoAnimateTime) + 1) / 2;
state.mouseY = (Math.cos(autoAnimateTime * 0.8) + 1) / 2;
} else if (!state.isMouseInside) {
state.mouseX += (state.targetX - state.mouseX) * options.idleSpeed;
state.mouseY += (state.targetY - state.mouseY) * options.idleSpeed;
}
const effectiveX = state.mouseX * options.mouseInfluence + 0.5 * (1 - options.mouseInfluence);
const effectiveY = state.mouseY * options.mouseInfluence + 0.5 * (1 - options.mouseInfluence);
const posX = effectiveX * 100;
const posY = effectiveY * 100;
element.gradientElement.style.backgroundPosition = posX + '% ' + posY + '%';
}
function smoothUpdate() {
updateGradient();
animationFrameId = requestAnimationFrame(smoothUpdate);
}
smoothUpdate();
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
if (!animationFrameId) {
smoothUpdate();
}
} else {
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
animationFrameId = null;
}
}
});
}, { threshold: 0 });
observer.observe(element);
element._cleanupGradient = () => {
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
observer.disconnect();
if (element.gradientElement && element.gradientElement.parentNode) {
element.gradientElement.parentNode.removeChild(element.gradientElement);
}
element.dataset.ambientInitialized = 'false';
};
}
function updateGradientPreview() {
const previews = ['gradient-preview'];
previews.forEach(previewId => {
const preview = document.getElementById(previewId);
if (!preview) return;
if (preview._cleanupGradient) {
preview._cleanupGradient();
}
preview.setAttribute('data-ambient-gradient', 'true');
if (gradientConfig.autoAnimate) {
preview.setAttribute('data-ambient-gradient-auto', 'true');
} else {
preview.removeAttribute('data-ambient-gradient-auto');
}
for (let i = 0; i < gradientConfig.colors.length; i++) {
preview.setAttribute(`data-ambient-gradient-color${i+1}`, gradientConfig.colors[i]);
}
preview.setAttribute('data-ambient-gradient-angle', gradientConfig.angle);
preview.setAttribute('data-ambient-gradient-size', gradientConfig.backgroundSize);
preview.setAttribute('data-ambient-gradient-auto-speed', gradientConfig.autoSpeed);
preview.setAttribute('data-ambient-gradient-mouse-influence', gradientConfig.mouseInfluence / 100);
preview.setAttribute('data-ambient-gradient-idle-speed', gradientConfig.idleSpeed);
preview.setAttribute('data-ambient-gradient-transition', gradientConfig.transitionSmoothness);
preview.setAttribute('data-ambient-gradient-z-index', gradientConfig.zIndex);
preview.setAttribute('data-ambient-gradient-center', gradientConfig.centerContent);
preview.setAttribute('data-ambient-gradient-border-inherit', gradientConfig.borderRadiusInherit);
});
initAmbientGradient();
}
function hexToHsl(hex) {
const r = parseInt(hex.slice(1, 3), 16) / 255;
const g = parseInt(hex.slice(3, 5), 16) / 255;
const b = parseInt(hex.slice(5, 7), 16) / 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return `hsl(${Math.round(h * 360)}, ${Math.round(s * 100)}%, ${Math.round(l * 100)}%)`;
}
function hslToHex(hsl) {
// Extraer valores de la cadena HSL
const match = hsl.match(/hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/);
if (!match) return null;
let h = parseInt(match[1]) / 360;
let s = parseInt(match[2]) / 100;
let l = parseInt(match[3]) / 100;
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
let r, g, b;
if (s === 0) {
r = g = b = l; // achromatic
} else {
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
const toHex = (c) => {
const hex = Math.round(c * 255).toString(16);
return hex.length === 1 ? '0' + hex : hex;
};
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}
function isValidHex(hex) {
return /^#[0-9A-F]{6}$/i.test(hex);
}
function isValidHsl(hsl) {
return /^hsl\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*\)$/i.test(hsl);
}
function formatHex(value) {
// Remover espacios y caracteres no válidos
let hex = value.replace(/[^0-9A-Fa-f#]/g, '');
// Agregar # si no lo tiene
if (!hex.startsWith('#')) {
hex = '#' + hex;
}
// Limitar a 7 caracteres (#RRGGBB)
if (hex.length > 7) {
hex = hex.substring(0, 7);
}
return hex.toUpperCase();
}
function formatHsl(value) {
// Limpiar la cadena y extraer números
const cleanValue = value.replace(/[^\d,\s]/g, '');
const numbers = cleanValue.match(/\d+/g);
if (!numbers || numbers.length < 3) {
// Si no hay suficientes números, intentar extraer de formato parcial
const partialMatch = value.match(/(\d+)/g);
if (partialMatch && partialMatch.length >= 1) {
const h = Math.min(360, Math.max(0, parseInt(partialMatch[0]) || 0));
const s = Math.min(100, Math.max(0, parseInt(partialMatch[1]) || 50));
const l = Math.min(100, Math.max(0, parseInt(partialMatch[2]) || 50));
return `hsl(${h}, ${s}%, ${l}%)`;
}
return value;
}
let h = Math.min(360, Math.max(0, parseInt(numbers[0])));
let s = Math.min(100, Math.max(0, parseInt(numbers[1])));
let l = Math.min(100, Math.max(0, parseInt(numbers[2])));
return `hsl(${h}, ${s}%, ${l}%)`;
}
function generateRandomColor() {
return '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0');
}
function showNotification(message, type = 'success') {
const notification = document.getElementById('notification');
notification.textContent = message;
notification.className = `notification ${type}`;
// Forzar un reflow para asegurar que el estado inicial se aplique
notification.offsetHeight;
// Mostrar la notificación
notification.style.visibility = 'visible';
notification.classList.add('show');
setTimeout(() => {
notification.classList.remove('show');
// Asegurar que esté completamente oculta después de la animación
setTimeout(() => {
if (!notification.classList.contains('show')) {
notification.style.visibility = 'hidden';
}
}, 400);
}, 3000);
}
// Función para generar IDs únicos de 6 caracteres
function generateUniqueId() {
return Math.random().toString(36).substring(2, 8);
}
function generateFullSectionJSON() {
// Generar IDs únicos para todos los elementos
const sectionId = generateUniqueId();
const container1Id = generateUniqueId();
const block1Id = generateUniqueId();
const div1Id = generateUniqueId();
const textBasic1Id = generateUniqueId();
const div2Id = generateUniqueId();
const headingId = generateUniqueId();
const textBasic2Id = generateUniqueId();
const container2Id = generateUniqueId();
const block2Id = generateUniqueId();
const div3Id = generateUniqueId();
const textBasic3Id = generateUniqueId();
const textBasic4Id = generateUniqueId();
const textBasic5Id = generateUniqueId();
const div4Id = generateUniqueId();
const textBasic6Id = generateUniqueId();
const textBasic7Id = generateUniqueId();
const textBasic8Id = generateUniqueId();
const div5Id = generateUniqueId();
const textBasic9Id = generateUniqueId();
const textBasic10Id = generateUniqueId();
const textBasic11Id = generateUniqueId();
const div6Id = generateUniqueId();
const textBasic12Id = generateUniqueId();
const codeId = generateUniqueId();
// Obtener el JavaScript actual con la configuración del usuario
const jsCode = generateJavaScriptCode();
// Crear el objeto JSON completo de Bricks Builder
const bricksJSON = {
"content": [
{
"id": sectionId,
"name": "section",
"parent": 0,
"children": [container1Id, container2Id, codeId],
"settings": {
"_justifyContent": "center",
"_background": {"color": {"hex": "#ffffff"}},
"_padding": {"top": "100", "right": "20", "bottom": "100", "left": "20"},
"_height:mobile_landscape": "auto",
"_padding:mobile_landscape": {"top": "50", "bottom": "50"}
},
"label": "Card 023"
},
{
"id": container1Id,
"name": "container",
"parent": sectionId,
"children": [block1Id],
"settings": {
"_rowGap": "50"
}
},
{
"id": block1Id,
"name": "block",
"parent": container1Id,
"children": [div1Id, div2Id],
"settings": {
"_rowGap": "30"
}
},
{
"id": div1Id,
"name": "div",
"parent": block1Id,
"children": [textBasic1Id],
"settings": {}
},
{
"id": textBasic1Id,
"name": "text-basic",
"parent": div1Id,
"children": [],
"settings": {
"text": "SERVICES",
"tag": "span",
"_typography": {
"color": {"hex": "#575757"},
"font-size": "14",
"font-weight": "400"
}
}
},
{
"id": div2Id,
"name": "div",
"parent": block1Id,
"children": [headingId, textBasic2Id],
"settings": {
"_display": "flex",
"_direction": "column",
"_rowGap": "20"
}
},
{
"id": headingId,
"name": "heading",
"parent": div2Id,
"children": [],
"settings": {
"text": "WHAT WE <span style=\"background:linear-gradient(90deg,purple,pink);-webkit-background-clip:text;color:transparent\">CRAFT</span>",
"_typography": {
"color": {"hex": "#000000"},
"font-weight": "500",
"font-size": "var(--h1)"
}
}
},
{
"id": textBasic2Id,
"name": "text-basic",
"parent": div2Id,
"children": [],
"settings": {
"text": "Three essential experiences that transform ideas into reality. Each service designed to push boundaries and create something extraordinary.\n\n",
"tag": "p",
"_typography": {
"color": {"hex": "#000000"},
"font-weight": "300",
"font-size": "18"
}
}
},
{
"id": container2Id,
"name": "container",
"parent": sectionId,
"children": [block2Id],
"settings": {}
},
{
"id": block2Id,
"name": "block",
"parent": container2Id,
"children": [div3Id, div5Id],
"settings": {
"_display": "grid",
"_gridTemplateColumns": "1fr 1fr",
"_gridGap": "30",
"_gridTemplateColumns:mobile_landscape": "1fr"
}
},
{
"id": div3Id,
"name": "div",
"parent": block2Id,
"children": [textBasic3Id, textBasic4Id, textBasic5Id, div4Id],
"settings": {
"_display": "flex",
"_direction": "column",
"_rowGap": "20",
"_padding": {"top": "20", "right": "20", "bottom": "20", "left": "20"},
"_border": {"radius": {"top": "15", "right": "15", "bottom": "15", "left": "15"}},
"_attributes": [{"id": generateUniqueId(), "name": "data-ambient-gradient"}],
"_justifyContent": "space-between"
},
"label": "Card 1"
},
{
"id": textBasic3Id,
"name": "text-basic",
"parent": div3Id,
"children": [],
"settings": {
"text": "01 — DIGITAL IDENTITY",
"_typography": {
"color": {"hex": "#f2a1e9"},
"font-weight": "300",
"font-size": "15"
},
"tag": "span"
}
},
{
"id": textBasic4Id,
"name": "text-basic",
"parent": div3Id,
"children": [],
"settings": {
"text": "Brand Universe",
"_typography": {
"color": {"hex": "#ffffff"},
"font-weight": "600",
"font-size": "30"
},
"tag": "span"
}
},
{
"id": textBasic5Id,
"name": "text-basic",
"parent": div3Id,
"children": [],
"settings": {
"text": "Complete visual identity systems that live and breathe across every touchpoint. From logo to motion, we create cohesive brand experiences.",
"tag": "p",
"_typography": {
"color": {"hex": "#ffffff"},
"font-weight": "300",
"font-size": "18"
}
}
},
{
"id": div4Id,
"name": "div",
"parent": div3Id,
"children": [textBasic6Id, textBasic7Id, textBasic8Id],
"settings": {
"_display": "flex",
"_columnGap": "20"
}
},
{
"id": textBasic6Id,
"name": "text-basic",
"parent": div4Id,
"children": [],
"settings": {
"text": "Logo Design",
"tag": "span",
"_typography": {
"color": {"hex": "#ffffff"},
"font-size": "14",
"font-weight": "400"
},
"_padding": {"left": "15", "right": "15", "top": "0", "bottom": "0"},
"_border": {"radius": {"top": "15", "right": "15", "bottom": "15", "left": "15"}},
"_background": {"color": {"hex": "#000000", "rgb": "rgba(0, 0, 0, 0.2)", "hsl": "hsla(0, 0%, 0%, 0.2)"}}
}
},
{
"id": textBasic7Id,
"name": "text-basic",
"parent": div4Id,
"children": [],
"settings": {
"text": "Brand Guidelines",
"tag": "span",
"_typography": {
"color": {"hex": "#ffffff"},
"font-size": "14",
"font-weight": "400"
},
"_padding": {"left": "15", "right": "15", "top": "0", "bottom": "0"},
"_border": {"radius": {"top": "15", "right": "15", "bottom": "15", "left": "15"}},
"_background": {"color": {"hex": "#000000", "rgb": "rgba(0, 0, 0, 0.2)", "hsl": "hsla(0, 0%, 0%, 0.2)"}}
}
},
{
"id": textBasic8Id,
"name": "text-basic",
"parent": div4Id,
"children": [],
"settings": {
"text": "Motion Graphics",
"tag": "span",
"_typography": {
"color": {"hex": "#ffffff"},
"font-size": "14",
"font-weight": "400"
},
"_padding": {"left": "15", "right": "15", "top": "0", "bottom": "0"},
"_border": {"radius": {"top": "15", "right": "15", "bottom": "15", "left": "15"}},
"_background": {"color": {"hex": "#000000", "rgb": "rgba(0, 0, 0, 0.2)", "hsl": "hsla(0, 0%, 0%, 0.2)"}}
}
},
{
"id": div5Id,
"name": "div",
"parent": block2Id,
"children": [textBasic9Id, textBasic10Id, textBasic11Id, div6Id],
"settings": {
"_display": "flex",
"_direction": "column",
"_rowGap": "20",
"_padding": {"top": "20", "right": "20", "bottom": "20", "left": "20"},
"_border": {"radius": {"top": "15", "right": "15", "bottom": "15", "left": "15"}},
"_attributes": [{"id": generateUniqueId(), "name": "data-ambient-gradient"}],
"_justifyContent": "space-between"
},
"label": "Card 2"
},
{
"id": textBasic9Id,
"name": "text-basic",
"parent": div5Id,
"children": [],
"settings": {
"text": "02 — DIGITAL PRESENCE",
"_typography": {
"color": {"hex": "#f2a1e9"},
"font-weight": "300",
"font-size": "15"
},
"tag": "span"
}
},
{
"id": textBasic10Id,
"name": "text-basic",
"parent": div5Id,
"children": [],
"settings": {
"text": "Web\nExperiences",
"_typography": {
"color": {"hex": "#ffffff"},
"font-weight": "600",
"font-size": "30"
},
"tag": "span"
}
},
{
"id": textBasic11Id,
"name": "text-basic",
"parent": div5Id,
"children": [],
"settings": {
"text": "Custom websites and web applications that perform beautifully and convert visitors into customers.",
"tag": "p",
"_typography": {
"color": {"hex": "#ffffff"},
"font-weight": "300",
"font-size": "18"
}
}
},
{
"id": div6Id,
"name": "div",
"parent": div5Id,
"children": [textBasic12Id],
"settings": {
"_display": "flex",
"_columnGap": "20"
}
},
{
"id": textBasic12Id,
"name": "text-basic",
"parent": div6Id,
"children": [],
"settings": {
"text": "$8000+",
"tag": "span",
"_typography": {
"color": {"hex": "#ffffff"},
"font-weight": "500",
"font-size": "20"
}
}
},
{
"id": codeId,
"name": "code",
"parent": sectionId,
"children": [],
"settings": {
"executeCode": true,
"javascriptCode": jsCode,
"_display": "none"
},
"label": "Ambient Gradient JS"
}
],
"source": "bricksCopiedElements",
"sourceUrl": "https://test.bricksfusion.com",
"version": "2.0.1",
"globalClasses": [],
"globalElements": []
};
return JSON.stringify(bricksJSON, null, 2);
}
function generateJavaScriptCode() {
const colors = gradientConfig.colors;
const angle = gradientConfig.angle;
const bgSize = gradientConfig.backgroundSize;
const autoAnimate = gradientConfig.autoAnimate;
const autoSpeed = gradientConfig.autoSpeed;
const mouseInfluence = gradientConfig.mouseInfluence / 100;
const idleSpeed = gradientConfig.idleSpeed;
const transitionSmoothness = gradientConfig.transitionSmoothness;
const zIndex = gradientConfig.zIndex;
const centerContent = gradientConfig.centerContent;
const borderRadiusInherit = gradientConfig.borderRadiusInherit;
return "(function() {\n" +
" const defaultConfig = {\n" +
" colors: " + JSON.stringify(colors) + ",\n" +
" angle: " + angle + ",\n" +
" backgroundSize: " + bgSize + ",\n" +
" autoAnimate: " + autoAnimate + ",\n" +
" autoSpeed: " + autoSpeed + ",\n" +
" mouseInfluence: " + mouseInfluence + ",\n" +
" idleSpeed: " + idleSpeed + ",\n" +
" transitionSmoothness: " + transitionSmoothness + ",\n" +
" zIndex: " + zIndex + ",\n" +
" centerContent: " + centerContent + ",\n" +
" borderRadiusInherit: " + borderRadiusInherit + "\n" +
" };\n" +
" \n" +
" function initAmbientGradient() {\n" +
" const sections = document.querySelectorAll('[data-ambient-gradient]:not([data-ambient-initialized=\"true\"])');\n" +
" \n" +
" sections.forEach((section) => {\n" +
" const isAutoAnimate = section.hasAttribute('data-ambient-gradient-auto') || defaultConfig.autoAnimate;\n" +
" \n" +
" let colors = [];\n" +
" for (let i = 1; i <= 4; i++) {\n" +
" const colorAttr = 'data-ambient-gradient-color' + i;\n" +
" if (section.hasAttribute(colorAttr)) {\n" +
" colors.push(section.getAttribute(colorAttr));\n" +
" }\n" +
" }\n" +
" \n" +
" if (colors.length === 0) {\n" +
" colors = defaultConfig.colors;\n" +
" }\n" +
" \n" +
" const angle = section.hasAttribute('data-ambient-gradient-angle') \n" +
" ? parseFloat(section.getAttribute('data-ambient-gradient-angle')) \n" +
" : defaultConfig.angle;\n" +
" \n" +
" const bgSize = section.hasAttribute('data-ambient-gradient-size') \n" +
" ? parseFloat(section.getAttribute('data-ambient-gradient-size')) \n" +
" : defaultConfig.backgroundSize;\n" +
" \n" +
" const autoSpeed = section.hasAttribute('data-ambient-gradient-auto-speed') \n" +
" ? parseFloat(section.getAttribute('data-ambient-gradient-auto-speed')) \n" +
" : defaultConfig.autoSpeed;\n" +
" \n" +
" const mouseInfluence = section.hasAttribute('data-ambient-gradient-mouse-influence') \n" +
" ? parseFloat(section.getAttribute('data-ambient-gradient-mouse-influence')) \n" +
" : defaultConfig.mouseInfluence;\n" +
" \n" +
" const idleSpeed = section.hasAttribute('data-ambient-gradient-idle-speed') \n" +
" ? parseFloat(section.getAttribute('data-ambient-gradient-idle-speed')) \n" +
" : defaultConfig.idleSpeed;\n" +
" \n" +
" const transitionSmoothness = section.hasAttribute('data-ambient-gradient-transition') \n" +
" ? parseFloat(section.getAttribute('data-ambient-gradient-transition')) \n" +
" : defaultConfig.transitionSmoothness;\n" +
" \n" +
" const zIndex = section.hasAttribute('data-ambient-gradient-z-index') \n" +
" ? parseInt(section.getAttribute('data-ambient-gradient-z-index')) \n" +
" : defaultConfig.zIndex;\n" +
" \n" +
" const centerContent = section.hasAttribute('data-ambient-gradient-center') \n" +
" ? section.getAttribute('data-ambient-gradient-center') === 'true' \n" +
" : defaultConfig.centerContent;\n" +
" \n" +
" const borderRadiusInherit = section.hasAttribute('data-ambient-gradient-border-inherit') \n" +
" ? section.getAttribute('data-ambient-gradient-border-inherit') === 'true' \n" +
" : defaultConfig.borderRadiusInherit;\n" +
" \n" +
" const options = {\n" +
" colors,\n" +
" angle,\n" +
" backgroundSize: bgSize,\n" +
" autoAnimate: isAutoAnimate,\n" +
" autoSpeed,\n" +
" mouseInfluence,\n" +
" idleSpeed,\n" +
" transitionSmoothness,\n" +
" zIndex,\n" +
" centerContent,\n" +
" borderRadiusInherit\n" +
" };\n" +
" \n" +
" const state = {\n" +
" mouseX: 0.5,\n" +
" mouseY: 0.5,\n" +
" targetX: 0.5,\n" +
" targetY: 0.5,\n" +
" isMouseInside: false,\n" +
" isAutoAnimate: isAutoAnimate\n" +
" };\n" +
" \n" +
" setupGradient(section, options);\n" +
" \n" +
" if (!isAutoAnimate) {\n" +
" setupEventListeners(section, state, options);\n" +
" }\n" +
" \n" +
" startAnimation(section, state, options);\n" +
" \n" +
" section.dataset.ambientInitialized = 'true';\n" +
" });\n" +
" }\n" +
" \n" +
" function setupGradient(element, options) {\n" +
" const gradientElement = document.createElement('div');\n" +
" \n" +
" gradientElement.style.position = 'absolute';\n" +
" gradientElement.style.top = '0';\n" +
" gradientElement.style.left = '0';\n" +
" gradientElement.style.width = '100%';\n" +
" gradientElement.style.height = '100%';\n" +
" \n" +
" gradientElement.style.background = 'linear-gradient(' + options.angle + 'deg, ' + options.colors.join(', ') + ')';\n" +
" gradientElement.style.backgroundSize = options.backgroundSize + '% ' + options.backgroundSize + '%';\n" +
" gradientElement.style.transition = 'background-position ' + options.transitionSmoothness + 's ease-out';\n" +
" gradientElement.style.zIndex = options.zIndex;\n" +
" \n" +
" if (options.borderRadiusInherit) {\n" +
" gradientElement.style.borderRadius = 'inherit';\n" +
" }\n" +
" \n" +
" const computedStyle = window.getComputedStyle(element);\n" +
" if (computedStyle.position === 'static') {\n" +
" element.style.position = 'relative';\n" +
" }\n" +
" \n" +
" if (element.firstChild) {\n" +
" element.insertBefore(gradientElement, element.firstChild);\n" +
" } else {\n" +
" element.appendChild(gradientElement);\n" +
" }\n" +
" \n" +
" element.gradientElement = gradientElement;\n" +
" \n" +
" if (options.centerContent) {\n" +
" Array.from(element.children).forEach(child => {\n" +
" if (child !== gradientElement) {\n" +
" if (getComputedStyle(child).position === 'static') {\n" +
" child.style.position = 'relative';\n" +
" }\n" +
" child.style.zIndex = '1';\n" +
" }\n" +
" });\n" +
" }\n" +
" }\n" +
" \n" +
" function setupEventListeners(element, state, options) {\n" +
" element.addEventListener('mousemove', (e) => {\n" +
" const rect = element.getBoundingClientRect();\n" +
" state.mouseX = (e.clientX - rect.left) / rect.width;\n" +
" state.mouseY = (e.clientY - rect.top) / rect.height;\n" +
" state.isMouseInside = true;\n" +
" });\n" +
" \n" +
" element.addEventListener('mouseleave', () => {\n" +
" state.isMouseInside = false;\n" +
" state.targetX = Math.random();\n" +
" state.targetY = Math.random();\n" +
" });\n" +
" \n" +
" element.addEventListener('mouseenter', () => {\n" +
" state.isMouseInside = true;\n" +
" });\n" +
" \n" +
" element.addEventListener('touchmove', (e) => {\n" +
" e.preventDefault();\n" +
" const touch = e.touches[0];\n" +
" const rect = element.getBoundingClientRect();\n" +
" state.mouseX = (touch.clientX - rect.left) / rect.width;\n" +
" state.mouseY = (touch.clientY - rect.top) / rect.height;\n" +
" state.isMouseInside = true;\n" +
" }, { passive: false });\n" +
" \n" +
" element.addEventListener('touchend', () => {\n" +
" state.isMouseInside = false;\n" +
" state.targetX = Math.random();\n" +
" state.targetY = Math.random();\n" +
" });\n" +
" \n" +
" element.addEventListener('focus', () => {\n" +
" state.isMouseInside = true;\n" +
" });\n" +
" \n" +
" element.addEventListener('blur', () => {\n" +
" state.isMouseInside = false;\n" +
" state.targetX = Math.random();\n" +
" state.targetY = Math.random();\n" +
" });\n" +
" }\n" +
" \n" +
" function startAnimation(element, state, options) {\n" +
" let animationFrameId;\n" +
" let autoAnimateTime = 0;\n" +
" \n" +
" function updateGradient() {\n" +
" if (state.isAutoAnimate) {\n" +
" autoAnimateTime += options.autoSpeed;\n" +
" state.mouseX = (Math.sin(autoAnimateTime) + 1) / 2;\n" +
" state.mouseY = (Math.cos(autoAnimateTime * 0.8) + 1) / 2;\n" +
" } \n" +
" else if (!state.isMouseInside) {\n" +
" state.mouseX += (state.targetX - state.mouseX) * options.idleSpeed;\n" +
" state.mouseY += (state.targetY - state.mouseY) * options.idleSpeed;\n" +
" }\n" +
" \n" +
" const effectiveX = state.mouseX * options.mouseInfluence + 0.5 * (1 - options.mouseInfluence);\n" +
" const effectiveY = state.mouseY * options.mouseInfluence + 0.5 * (1 - options.mouseInfluence);\n" +
" \n" +
" const posX = effectiveX * 100;\n" +
" const posY = effectiveY * 100;\n" +
" element.gradientElement.style.backgroundPosition = posX + '% ' + posY + '%';\n" +
" }\n" +
" \n" +
" function smoothUpdate() {\n" +
" updateGradient();\n" +
" animationFrameId = requestAnimationFrame(smoothUpdate);\n" +
" }\n" +
" \n" +
" smoothUpdate();\n" +
" \n" +
" const observer = new IntersectionObserver((entries) => {\n" +
" entries.forEach(entry => {\n" +
" if (entry.isIntersecting) {\n" +
" if (!animationFrameId) {\n" +
" smoothUpdate();\n" +
" }\n" +
" } else {\n" +
" if (animationFrameId) {\n" +
" cancelAnimationFrame(animationFrameId);\n" +
" animationFrameId = null;\n" +
" }\n" +
" }\n" +
" });\n" +
" }, { threshold: 0 });\n" +
" \n" +
" observer.observe(element);\n" +
" \n" +
" element._cleanupGradient = () => {\n" +
" if (animationFrameId) {\n" +
" cancelAnimationFrame(animationFrameId);\n" +
" }\n" +
" observer.disconnect();\n" +
" if (element.gradientElement && element.gradientElement.parentNode) {\n" +
" element.gradientElement.parentNode.removeChild(element.gradientElement);\n" +
" }\n" +
" element.dataset.ambientInitialized = 'false';\n" +
" };\n" +
" }\n" +
" \n" +
" if (document.readyState === 'loading') {\n" +
" document.addEventListener('DOMContentLoaded', initAmbientGradient);\n" +
" } else {\n" +
" initAmbientGradient();\n" +
" }\n" +
" \n" +
" document.addEventListener('bricks-lazy-load', initAmbientGradient);\n" +
"})();";
}
function copyJsToClipboard() {
const jsCode = generateJavaScriptCode();
navigator.clipboard.writeText(jsCode)
.then(() => {
showNotification('JavaScript code copied to clipboard!');
})
.catch(err => {
// Fallback para navegadores que no soporten clipboard API
try {
const textArea = document.createElement('textarea');
textArea.value = jsCode;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
showNotification('JavaScript code copied to clipboard!');
} catch (fallbackErr) {
showNotification('Failed to copy to clipboard. Please try again.', 'error');
}
});
}
function copyFullSectionToClipboard() {
const sectionJSON = generateFullSectionJSON();
navigator.clipboard.writeText(sectionJSON)
.then(() => {
showNotification('Full section JSON copied to clipboard!');
})
.catch(err => {
// Fallback para navegadores que no soporten clipboard API
try {
const textArea = document.createElement('textarea');
textArea.value = sectionJSON;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
showNotification('Full section JSON copied to clipboard!');
} catch (fallbackErr) {
showNotification('Failed to copy to clipboard. Please try again.', 'error');
}
});
}
function downloadJsFile() {
const jsCode = generateJavaScriptCode();
const blob = new Blob([jsCode], { type: 'application/javascript' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'ambient-gradient.js';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showNotification('JavaScript file downloaded successfully!');
}
function copyToClipboard(text) {
navigator.clipboard.writeText(text)
.then(() => {
showNotification('Copied to clipboard!');
})
.catch(err => {
showNotification('Failed to copy to clipboard', 'error');
});
}
window.resetParameter = function(parameterId, defaultValue) {
const element = document.getElementById(parameterId);
if (element) {
element.value = defaultValue;
const valueElement = document.getElementById(`${parameterId}-value`);
if (valueElement) {
valueElement.textContent = defaultValue;
}
switch (parameterId) {
case 'gradient-angle':
gradientConfig.angle = defaultValue;
break;
case 'bg-size':
gradientConfig.backgroundSize = defaultValue;
break;
case 'auto-speed':
gradientConfig.autoSpeed = defaultValue;
break;
case 'mouse-influence':
gradientConfig.mouseInfluence = defaultValue;
break;
case 'idle-speed':
gradientConfig.idleSpeed = defaultValue;
break;
case 'transition-smoothness':
gradientConfig.transitionSmoothness = defaultValue;
break;
case 'z-index':
gradientConfig.zIndex = defaultValue;
break;
}
updateGradientPreview();
showNotification(`${parameterId.replace(/-/g, ' ')} reset to default`);
}
};
function resetAllSettings() {
gradientConfig = { ...defaultConfig };
document.getElementById('color1').value = defaultConfig.colors[0];
document.getElementById('color2').value = defaultConfig.colors[1];
document.getElementById('color3').value = defaultConfig.colors[2];
document.getElementById('color4').value = defaultConfig.colors[3];
document.getElementById('gradient-angle').value = defaultConfig.angle;
document.getElementById('bg-size').value = defaultConfig.backgroundSize;
document.getElementById('auto-animate').checked = defaultConfig.autoAnimate;
document.getElementById('auto-speed').value = defaultConfig.autoSpeed;
document.getElementById('mouse-influence').value = defaultConfig.mouseInfluence;
document.getElementById('idle-speed').value = defaultConfig.idleSpeed;
document.getElementById('transition-smoothness').value = defaultConfig.transitionSmoothness;
document.getElementById('z-index').value = defaultConfig.zIndex;
document.getElementById('center-content').checked = defaultConfig.centerContent;
document.getElementById('border-radius-inherit').checked = defaultConfig.borderRadiusInherit;
document.getElementById('angle-value').textContent = defaultConfig.angle;
document.getElementById('bg-size-value').textContent = defaultConfig.backgroundSize;
document.getElementById('auto-speed-value').textContent = defaultConfig.autoSpeed;
document.getElementById('mouse-influence-value').textContent = defaultConfig.mouseInfluence;
document.getElementById('idle-speed-value').textContent = defaultConfig.idleSpeed;
document.getElementById('smoothness-value').textContent = defaultConfig.transitionSmoothness;
document.getElementById('z-index-value').textContent = defaultConfig.zIndex;
updateColorInputs();
updateGradientPreview();
showNotification('All settings reset to default');
}
function generateRandomGradient() {
const randomColors = Array.from({ length: 4 }, () => generateRandomColor());
gradientConfig.colors = randomColors;
gradientConfig.angle = Math.floor(Math.random() * 360);
randomColors.forEach((color, index) => {
const colorInput = document.getElementById(`color${index + 1}`);
const hexInput = document.getElementById(`color${index + 1}-hex`);
const hslInput = document.getElementById(`color${index + 1}-hsl`);
colorInput.value = color;
hexInput.value = color;
hslInput.value = hexToHsl(color);
// Actualizar el fondo del contenedor del selector de color
const container = colorInput.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', color);
}
});
document.getElementById('gradient-angle').value = gradientConfig.angle;
document.getElementById('angle-value').textContent = gradientConfig.angle;
updateGradientPreview();
showNotification('Random gradient generated!');
}
function updateColorInputs() {
for (let i = 1; i <= 4; i++) {
const color = gradientConfig.colors[i - 1];
const colorInput = document.getElementById(`color${i}`);
const hexInput = document.getElementById(`color${i}-hex`);
const hslInput = document.getElementById(`color${i}-hsl`);
if (colorInput && hexInput && hslInput) {
colorInput.value = color;
hexInput.value = color;
hslInput.value = hexToHsl(color);
// Actualizar el fondo del contenedor del selector de color
const container = colorInput.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', color);
}
// Remover clases de error al actualizar
hexInput.classList.remove('invalid');
hslInput.classList.remove('invalid');
}
}
}
function initializeUI() {
initAmbientGradient();
const instructionsToggle = document.getElementById('instructions-toggle');
const instructionsContent = document.getElementById('instructions-content');
const instructionsCard = document.getElementById('instructions-card');
const toggleIcon = instructionsToggle.querySelector('.toggle-icon');
instructionsToggle.addEventListener('click', () => {
const isVisible = instructionsContent.classList.contains('show');
if (isVisible) {
instructionsContent.classList.remove('show');
instructionsCard.classList.remove('expanded');
toggleIcon.classList.remove('expanded');
} else {
instructionsContent.classList.add('show');
instructionsCard.classList.add('expanded');
toggleIcon.classList.add('expanded');
}
});
document.getElementById('quick-attribute').addEventListener('click', () => {
copyToClipboard('data-ambient-gradient');
});
document.getElementById('download-config').addEventListener('click', () => {
copyJsToClipboard();
});
document.getElementById('copy-full-section').addEventListener('click', () => {
copyFullSectionToClipboard();
});
document.getElementById('randomize-gradient').addEventListener('click', () => {
generateRandomGradient();
});
document.getElementById('reset-colors').addEventListener('click', () => {
gradientConfig.colors = [...defaultConfig.colors];
updateColorInputs();
updateGradientPreview();
showNotification('Colors reset to default');
});
document.getElementById('reset-animation').addEventListener('click', () => {
gradientConfig.autoAnimate = defaultConfig.autoAnimate;
gradientConfig.autoSpeed = defaultConfig.autoSpeed;
gradientConfig.mouseInfluence = defaultConfig.mouseInfluence;
gradientConfig.idleSpeed = defaultConfig.idleSpeed;
gradientConfig.transitionSmoothness = defaultConfig.transitionSmoothness;
document.getElementById('auto-animate').checked = defaultConfig.autoAnimate;
document.getElementById('auto-speed').value = defaultConfig.autoSpeed;
document.getElementById('mouse-influence').value = defaultConfig.mouseInfluence;
document.getElementById('idle-speed').value = defaultConfig.idleSpeed;
document.getElementById('transition-smoothness').value = defaultConfig.transitionSmoothness;
document.getElementById('auto-speed-value').textContent = defaultConfig.autoSpeed;
document.getElementById('mouse-influence-value').textContent = defaultConfig.mouseInfluence;
document.getElementById('idle-speed-value').textContent = defaultConfig.idleSpeed;
document.getElementById('smoothness-value').textContent = defaultConfig.transitionSmoothness;
updateGradientPreview();
showNotification('Animation settings reset');
});
document.getElementById('reset-advanced').addEventListener('click', () => {
gradientConfig.zIndex = defaultConfig.zIndex;
gradientConfig.centerContent = defaultConfig.centerContent;
gradientConfig.borderRadiusInherit = defaultConfig.borderRadiusInherit;
document.getElementById('z-index').value = defaultConfig.zIndex;
document.getElementById('center-content').checked = defaultConfig.centerContent;
document.getElementById('border-radius-inherit').checked = defaultConfig.borderRadiusInherit;
document.getElementById('z-index-value').textContent = defaultConfig.zIndex;
updateGradientPreview();
showNotification('Advanced settings reset');
});
const colorInputs = document.querySelectorAll('input[type="color"]');
colorInputs.forEach((input, index) => {
const colorIndex = index + 1;
const hexInput = document.getElementById(`color${colorIndex}-hex`);
const hslInput = document.getElementById(`color${colorIndex}-hsl`);
// Inicializar valores HSL y el fondo del contenedor
hslInput.value = hexToHsl(input.value);
// Inicializar el fondo del contenedor del selector de color
const container = input.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', input.value);
}
// Event listener para el selector visual de color
input.addEventListener('input', () => {
const color = input.value;
hexInput.value = color;
hslInput.value = hexToHsl(color);
hexInput.classList.remove('invalid');
hslInput.classList.remove('invalid');
// Actualizar el fondo del contenedor del selector de color
const container = input.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', color);
}
gradientConfig.colors[index] = color;
updateGradientPreview();
});
// Event listener para el input HEX con validación en tiempo real
hexInput.addEventListener('input', (e) => {
let hex = e.target.value;
// Auto-formatear mientras se escribe
hex = formatHex(hex);
e.target.value = hex;
// Validar y actualizar
if (isValidHex(hex)) {
input.value = hex;
hslInput.value = hexToHsl(hex);
// Actualizar el fondo del contenedor del selector de color
const container = input.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', hex);
}
gradientConfig.colors[index] = hex;
e.target.classList.remove('invalid');
hslInput.classList.remove('invalid');
updateGradientPreview();
} else {
e.target.classList.add('invalid');
}
});
hexInput.addEventListener('blur', (e) => {
if (!isValidHex(e.target.value)) {
e.target.value = input.value;
e.target.classList.remove('invalid');
}
});
// Event listener para el input HSL con validación en tiempo real
hslInput.addEventListener('input', (e) => {
let hsl = e.target.value;
// Validar y actualizar si es válido
if (isValidHsl(hsl)) {
const hex = hslToHex(hsl);
if (hex) {
input.value = hex;
hexInput.value = hex;
// Actualizar el fondo del contenedor del selector de color
const container = input.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', hex);
}
gradientConfig.colors[index] = hex;
e.target.classList.remove('invalid');
hexInput.classList.remove('invalid');
updateGradientPreview();
return;
}
}
// Si no es válido, marcar como inválido pero permitir seguir escribiendo
e.target.classList.add('invalid');
});
hslInput.addEventListener('blur', (e) => {
let hsl = e.target.value;
// Intentar auto-formatear en el blur
if (!isValidHsl(hsl) && hsl.trim()) {
const formatted = formatHsl(hsl);
if (isValidHsl(formatted)) {
e.target.value = formatted;
const hex = hslToHex(formatted);
if (hex) {
input.value = hex;
hexInput.value = hex;
// Actualizar el fondo del contenedor del selector de color
const container = input.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', hex);
}
gradientConfig.colors[index] = hex;
e.target.classList.remove('invalid');
hexInput.classList.remove('invalid');
updateGradientPreview();
return;
}
}
}
// Si sigue siendo inválido, restaurar valor válido
if (!isValidHsl(e.target.value)) {
e.target.value = hexToHsl(input.value);
e.target.classList.remove('invalid');
}
});
});
const rangeInputs = document.querySelectorAll('input[type="range"]');
rangeInputs.forEach(input => {
let valueElement;
// Mapear los IDs correctos para los elementos value
switch (input.id) {
case 'gradient-angle':
valueElement = document.getElementById('angle-value');
break;
case 'transition-smoothness':
valueElement = document.getElementById('smoothness-value');
break;
default:
valueElement = document.getElementById(`${input.id}-value`);
}
if (valueElement) {
valueElement.textContent = input.value;
}
input.addEventListener('input', () => {
if (valueElement) {
valueElement.textContent = input.value;
}
switch (input.id) {
case 'gradient-angle':
gradientConfig.angle = parseFloat(input.value);
break;
case 'bg-size':
gradientConfig.backgroundSize = parseFloat(input.value);
break;
case 'auto-speed':
gradientConfig.autoSpeed = parseFloat(input.value);
break;
case 'mouse-influence':
gradientConfig.mouseInfluence = parseFloat(input.value);
break;
case 'idle-speed':
gradientConfig.idleSpeed = parseFloat(input.value);
break;
case 'transition-smoothness':
gradientConfig.transitionSmoothness = parseFloat(input.value);
break;
case 'z-index':
gradientConfig.zIndex = parseInt(input.value);
break;
}
updateGradientPreview();
});
});
document.getElementById('auto-animate').addEventListener('change', function() {
gradientConfig.autoAnimate = this.checked;
document.getElementById('auto-animation-settings').style.display =
this.checked ? 'block' : 'none';
document.getElementById('mouse-animation-settings').style.display =
this.checked ? 'none' : 'block';
updateGradientPreview();
});
document.getElementById('center-content').addEventListener('change', function() {
gradientConfig.centerContent = this.checked;
updateGradientPreview();
});
document.getElementById('border-radius-inherit').addEventListener('change', function() {
gradientConfig.borderRadiusInherit = this.checked;
updateGradientPreview();
});
document.getElementById('auto-animation-settings').style.display =
document.getElementById('auto-animate').checked ? 'block' : 'none';
document.getElementById('mouse-animation-settings').style.display =
document.getElementById('auto-animate').checked ? 'none' : 'block';
document.addEventListener('keydown', (e) => {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
return;
}
if (e.ctrlKey || e.metaKey) {
switch (e.key.toLowerCase()) {
case 'd':
e.preventDefault();
// Verificar protección antes de ejecutar
const downloadBtn = document.getElementById('download-config');
if (downloadBtn && downloadBtn.hasAttribute('data-protection-animation')) {
// Simular click en el botón para que el backend maneje la protección
downloadBtn.click();
} else {
copyJsToClipboard();
}
break;
case 's':
e.preventDefault();
// Verificar protección antes de ejecutar
const fullSectionBtn = document.getElementById('copy-full-section');
if (fullSectionBtn && fullSectionBtn.hasAttribute('data-protection-animation')) {
// Simular click en el botón para que el backend maneje la protección
fullSectionBtn.click();
} else {
copyFullSectionToClipboard();
}
break;
}
} else {
switch (e.key.toLowerCase()) {
case 'r':
generateRandomGradient();
break;
}
}
});
updateColorInputs();
setTimeout(() => {
showNotification('BricksFusion Ambient Gradient Configurator loaded!');
}, 500);
function saveConfiguration() {
try {
localStorage.setItem('bricksfusion-gradient-config', JSON.stringify(gradientConfig));
} catch (e) {
}
}
function loadConfiguration() {
try {
const saved = localStorage.getItem('bricksfusion-gradient-config');
if (saved) {
const savedConfig = JSON.parse(saved);
Object.assign(gradientConfig, savedConfig);
savedConfig.colors.forEach((color, index) => {
document.getElementById(`color${index + 1}`).value = color;
document.getElementById(`color${index + 1}-hex`).value = color;
document.getElementById(`color${index + 1}-hsl`).value = hexToHsl(color);
});
document.getElementById('gradient-angle').value = savedConfig.angle;
document.getElementById('bg-size').value = savedConfig.backgroundSize;
document.getElementById('auto-animate').checked = savedConfig.autoAnimate;
document.getElementById('auto-speed').value = savedConfig.autoSpeed;
document.getElementById('mouse-influence').value = savedConfig.mouseInfluence;
document.getElementById('idle-speed').value = savedConfig.idleSpeed;
document.getElementById('transition-smoothness').value = savedConfig.transitionSmoothness;
document.getElementById('z-index').value = savedConfig.zIndex;
document.getElementById('center-content').checked = savedConfig.centerContent;
document.getElementById('border-radius-inherit').checked = savedConfig.borderRadiusInherit;
document.getElementById('angle-value').textContent = savedConfig.angle;
document.getElementById('bg-size-value').textContent = savedConfig.backgroundSize;
document.getElementById('auto-speed-value').textContent = savedConfig.autoSpeed;
document.getElementById('mouse-influence-value').textContent = savedConfig.mouseInfluence;
document.getElementById('idle-speed-value').textContent = savedConfig.idleSpeed;
document.getElementById('smoothness-value').textContent = savedConfig.transitionSmoothness;
document.getElementById('z-index-value').textContent = savedConfig.zIndex;
updateGradientPreview();
}
} catch (e) {
}
}
const originalUpdateGradientPreview = updateGradientPreview;
updateGradientPreview = function() {
originalUpdateGradientPreview();
saveConfiguration();
};
loadConfiguration();
let performanceMonitor = {
animationFrames: 0,
lastCheck: Date.now(),
checkPerformance() {
this.animationFrames++;
const now = Date.now();
if (now - this.lastCheck >= 1000) {
const fps = this.animationFrames;
this.animationFrames = 0;
this.lastCheck = now;
if (fps < 30) {
// Low FPS detected
}
}
}
};
setInterval(() => {
performanceMonitor.checkPerformance();
}, 1000);
const observeElements = () => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.remove('loading');
}
});
});
document.querySelectorAll('.card').forEach(card => {
observer.observe(card);
});
};
observeElements();
function initDragAndDrop() {
const colorGrid = document.querySelector('.color-grid');
if (colorGrid) {
colorGrid.addEventListener('dragover', (e) => {
e.preventDefault();
colorGrid.style.backgroundColor = 'rgba(239, 96, 19, 0.1)';
});
colorGrid.addEventListener('dragleave', () => {
colorGrid.style.backgroundColor = '';
});
colorGrid.addEventListener('drop', (e) => {
e.preventDefault();
colorGrid.style.backgroundColor = '';
showNotification('Drag & drop feature coming soon!');
});
}
}
initDragAndDrop();
function initTooltips() {
const helpTooltips = document.querySelectorAll('.help-tooltip');
helpTooltips.forEach(tooltip => {
tooltip.addEventListener('mouseenter', (e) => {
const title = e.target.getAttribute('title');
if (title) {
const tooltipEl = document.createElement('div');
tooltipEl.className = 'custom-tooltip';
tooltipEl.textContent = title;
tooltipEl.style.cssText = `
position: fixed;
background: var(--card-bg);
color: var(--text-primary);
padding: 0.5rem 0.75rem;
border-radius: 6px;
font-size: 12px;
z-index: 1002;
border: 1px solid var(--border);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
max-width: 200px;
word-wrap: break-word;
`;
const rect = e.target.getBoundingClientRect();
tooltipEl.style.left = (rect.left + rect.width / 2 - 100) + 'px';
tooltipEl.style.top = (rect.top - 40) + 'px';
document.body.appendChild(tooltipEl);
e.target._tooltip = tooltipEl;
}
});
tooltip.addEventListener('mouseleave', (e) => {
if (e.target._tooltip) {
document.body.removeChild(e.target._tooltip);
e.target._tooltip = null;
}
});
});
}
initTooltips();
function exportConfiguration() {
const configData = {
version: '1.0',
timestamp: new Date().toISOString(),
configuration: gradientConfig
};
const blob = new Blob([JSON.stringify(configData, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'bricksfusion-gradient-config.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showNotification('Configuration exported successfully!');
}
function importConfiguration(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (e) => {
try {
const configData = JSON.parse(e.target.result);
if (configData.configuration) {
Object.assign(gradientConfig, configData.configuration);
updateColorInputs();
updateGradientPreview();
showNotification('Configuration imported successfully!');
}
} catch (error) {
showNotification('Invalid configuration file', 'error');
}
};
reader.readAsText(file);
}
function handleResize() {
if (window.innerWidth <= 768) {
document.body.style.paddingBottom = 'calc(var(--action-bar-height) + 20px)';
} else {
document.body.style.paddingBottom = 'var(--action-bar-height)';
}
}
window.addEventListener('resize', handleResize);
handleResize();
function smoothScrollTo(target) {
const element = document.querySelector(target);
if (element) {
element.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
}
function initAccessibility() {
document.getElementById('gradient-preview').setAttribute('aria-label', 'Ambient gradient preview');
document.getElementById('quick-attribute').setAttribute('aria-label', 'Copy data attribute to clipboard');
}
initAccessibility();
function handleErrors() {
window.addEventListener('error', (e) => {
showNotification('An error occurred. Please refresh.', 'error');
});
window.addEventListener('unhandledrejection', (e) => {
showNotification('A network error occurred.', 'error');
});
}
handleErrors();
function checkVersion() {
const currentVersion = '1.0.0';
const storedVersion = localStorage.getItem('bricksfusion-configurator-version');
if (storedVersion && storedVersion !== currentVersion) {
showNotification('Configurator updated!', 'info');
}
localStorage.setItem('bricksfusion-configurator-version', currentVersion);
}
checkVersion();
function trackEvent(eventName, eventData = {}) {
// Event tracking (silent)
}
document.getElementById('download-config').addEventListener('click', () => {
trackEvent('download_js', { colors: gradientConfig.colors.length });
});
document.getElementById('copy-full-section').addEventListener('click', () => {
trackEvent('copy_full_section', { colors: gradientConfig.colors.length });
});
document.getElementById('randomize-gradient').addEventListener('click', () => {
trackEvent('randomize_gradient');
});
}
initializeUI();
});
</script>
</body>
</html>
Ambient Gradient
Adds an animated gradient background that follows your visitor's mouse. Great for hero sections and interactive cards.
This is the effect in action
Colors
Pick up to 4 colors for your gradient. They'll blend together smoothly. Use fewer colors for a simple look, or all 4 for more variety.
Default: Purple, Pink, Blue, Teal
Settings
Changes the direction the gradient flows. 0 is bottom to top, 90 is left to right, 180 is top to bottom.
Default: 45
How big the gradient is. Bigger values = more dramatic color changes when it moves. Try 300-500 for nice results.
Default: 400
How fast the gradient moves to new positions. Lower = snappy, higher = smooth and flowing.
Default: 0.5
Animation
Turn this on to make the gradient move by itself without needing mouse interaction. Perfect for backgrounds.
Default: Off
How fast the automatic animation moves. Only works when Auto Animation is on. Keep it low (0.003-0.007) for subtle movement.
Default: 0.005
How much the mouse affects the gradient. 1.0 = full control, 0.5 = subtle influence, 0.0 = no mouse effect.
Default: 1.0
When the mouse leaves, the gradient drifts slowly at this speed. Keeps it from looking frozen.
Default: 0.05
Advanced
Controls if the gradient is behind or in front of your content. Keep it at 1 so text stays visible on top.
Default: 1
Automatically puts all your text and images above the gradient. Keep this on unless you know what you're doing.
Default: On
Makes the gradient match any rounded corners on your element. Keep it on for a polished look.
Default: On
Performance
This element is lightweight and won't slow down your site. It only animates when visible on screen and pauses when scrolled out of view.