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>Status Pulse 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: 800px;
padding: 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;
}
.attributes-table {
margin-top: 1rem;
width: 100%;
border-collapse: collapse;
font-size: var(--text-xs);
}
.attributes-table th,
.attributes-table td {
text-align: left;
padding: 0.5rem;
border-bottom: 1px solid var(--border);
}
.attributes-table th {
color: var(--accent);
font-weight: 600;
}
.attributes-table td {
color: var(--text-secondary);
}
.attributes-table code {
background-color: rgba(50, 50, 50, 0.5);
padding: 0.2rem 0.4rem;
border-radius: 3px;
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
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: #252525;
border: 1px solid var(--border);
box-shadow: var(--shadow);
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
.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: relative;
z-index: 2;
display: inline-flex;
align-items: center;
font-size: 1.75rem;
color: var(--text-primary);
font-weight: 500;
}
.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-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-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, #22c55e);
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);
}
input[type="text"],
input[type="number"],
select {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--border);
border-radius: var(--input-radius);
font-family: var(--font);
font-size: var(--text-xs);
color: var(--text-primary);
background-color: var(--card-bg);
margin-bottom: 0.75rem;
outline: none;
transition: var(--transition);
}
input[type="text"]:focus,
input[type="number"]:focus,
select:focus {
border-color: var(--accent);
box-shadow: 0 0 0 2px rgba(239, 96, 19, 0.2);
}
.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;
}
.status-pulse-dot {
position: relative;
display: inline-block;
border-radius: 50%;
vertical-align: middle;
}
@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);
}
</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/visual-effects/" class="breadcrumb-item">Visual effects</a>
<span class="breadcrumb-separator">›</span>
<span class="breadcrumb-item active">Status Pulse</span>
</nav>
<div class="action-buttons">
<div class="data-attribute-display" id="quick-attribute" title="Click to copy data attribute">
data-pulse-dot
</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">Status Pulse</h1>
<p class="page-subtitle">Interactive status indicators 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 & Customization Attributes
</div>
<span class="toggle-icon">▼</span>
</div>
<div class="instructions-content" id="instructions-content">
<div class="how-to-use">
<ol>
<li>Customize your status pulse effect 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 the JavaScript code</li>
<li>To add the effect to any text element: go to <strong>Section → Style → Attributes</strong>, add <code>data-pulse-dot</code> as attribute name (leave value empty)</li>
<li><strong>NEW:</strong> You can now customize each pulse individually using the attributes below!</li>
</ol>
<h3 style="margin-top: 1.5rem; margin-bottom: 1rem; color: var(--accent);">Individual Element Customization</h3>
<p style="margin-bottom: 1rem; color: var(--text-secondary);">Each element with <code>data-pulse-dot</code> can be customized using these additional attributes:</p>
<table class="attributes-table">
<thead>
<tr>
<th>Attribute</th>
<th>Description</th>
<th>Example Value</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>data-pulse-size</code></td>
<td>Size of the dot in pixels</td>
<td><code>12</code></td>
</tr>
<tr>
<td><code>data-pulse-color</code></td>
<td>Color of the pulse</td>
<td><code>#ff0000</code></td>
</tr>
<tr>
<td><code>data-pulse-speed</code></td>
<td>Animation speed in seconds</td>
<td><code>2.0</code></td>
</tr>
<tr>
<td><code>data-pulse-scale</code></td>
<td>Maximum scale of pulse</td>
<td><code>2.5</code></td>
</tr>
<tr>
<td><code>data-pulse-opacity</code></td>
<td>Core opacity (0-100)</td>
<td><code>80</code></td>
</tr>
<tr>
<td><code>data-pulse-spacing</code></td>
<td>Space between dot and text</td>
<td><code>12</code></td>
</tr>
<tr>
<td><code>data-pulse-shadow-enabled</code></td>
<td>Enable/disable glow (true/false)</td>
<td><code>false</code></td>
</tr>
<tr>
<td><code>data-pulse-shadow-blur</code></td>
<td>Glow intensity in pixels</td>
<td><code>15</code></td>
</tr>
<tr>
<td><code>data-pulse-animation-style</code></td>
<td>Animation type</td>
<td><code>grow</code>, <code>fade</code>, <code>both</code>, <code>double</code></td>
</tr>
<tr>
<td><code>data-pulse-timing-function</code></td>
<td>CSS timing function</td>
<td><code>ease-in-out</code></td>
</tr>
<tr>
<td><code>data-pulse-pause-hover</code></td>
<td>Pause on hover (true/false)</td>
<td><code>true</code></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="content">
<section class="preview-section">
<div class="preview-container" id="pulse-preview" data-pulse-dot="true">
<div class="preview-content">Interactive Status Pulse Preview</div>
<div class="preview-controls">
<button class="preview-btn" id="randomize-pulse" title="Randomize (R)">🎲</button>
</div>
</div>
</section>
<section class="controls-section">
<div class="card">
<div class="card-heading">
Pulse Appearance
<div class="card-actions">
<button class="card-action-btn" id="reset-appearance" title="Reset Appearance">↺</button>
</div>
</div>
<div class="card-content">
<div class="control-group">
<div class="control-label">
<span class="label-text">
Pulse Size
<span class="help-tooltip" title="Size of the status indicator dot">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="pulse-size-value">8</span>px</span>
<button class="reset-btn" onclick="resetParameter('pulse-size', 8)">↺</button>
</div>
</div>
<input type="range" id="pulse-size" min="4" max="20" step="1" value="8">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Pulse Color</span>
</div>
<div class="color-row">
<div class="color-picker-container">
<input type="color" id="pulse-color" value="#22c55e">
</div>
<div class="color-input-group">
<span class="color-label">HEX</span>
<input type="text" class="color-input hex-input" id="pulse-color-hex" value="#22c55e" placeholder="#FFFFFF">
</div>
<div class="color-input-group">
<span class="color-label">HSL</span>
<input type="text" class="color-input hsl-input" id="pulse-color-hsl" placeholder="hsl(0, 100%, 50%)">
</div>
</div>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Core Opacity
<span class="help-tooltip" title="Opacity of the main dot">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="pulse-opacity-value">100</span>%</span>
<button class="reset-btn" onclick="resetParameter('pulse-opacity', 100)">↺</button>
</div>
</div>
<input type="range" id="pulse-opacity" min="50" max="100" step="5" value="100">
</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="control-group">
<div class="control-label">
<span class="label-text">
Animation Speed
<span class="help-tooltip" title="Duration of one pulse cycle">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="pulse-speed-value">1.5</span>s</span>
<button class="reset-btn" onclick="resetParameter('pulse-speed', 1.5)">↺</button>
</div>
</div>
<input type="range" id="pulse-speed" min="0.5" max="3" step="0.1" value="1.5">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Pulse Scale
<span class="help-tooltip" title="Maximum size of the pulse effect">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="pulse-scale-value">2</span>x</span>
<button class="reset-btn" onclick="resetParameter('pulse-scale', 2)">↺</button>
</div>
</div>
<input type="range" id="pulse-scale" min="1.5" max="3" step="0.1" value="2">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Animation Style</span>
</div>
<select id="animation-style">
<option value="fade" selected>Fade Out</option>
<option value="grow">Grow Only</option>
<option value="both">Grow and Fade</option>
<option value="double">Double Pulse</option>
</select>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Timing Function</span>
</div>
<select id="timing-function">
<option value="cubic-bezier(0.4, 0, 0.6, 1)" selected>Smooth</option>
<option value="linear">Linear</option>
<option value="ease-in-out">Ease In Out</option>
<option value="cubic-bezier(0.68, -0.55, 0.265, 1.55)">Bounce</option>
</select>
</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="switch-container">
<span class="switch-label">Enable Glow Shadow</span>
<label class="switch">
<input type="checkbox" id="shadow-enabled" checked>
<span class="slider"></span>
</label>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Shadow Blur
<span class="help-tooltip" title="Intensity of the glow effect">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="shadow-blur-value">10</span>px</span>
<button class="reset-btn" onclick="resetParameter('shadow-blur', 10)">↺</button>
</div>
</div>
<input type="range" id="shadow-blur" min="0" max="20" step="1" value="10">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Spacing
<span class="help-tooltip" title="Space between pulse and text">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="spacing-value">8</span>px</span>
<button class="reset-btn" onclick="resetParameter('spacing', 8)">↺</button>
</div>
</div>
<input type="range" id="spacing" min="4" max="16" step="1" value="8">
</div>
<div class="switch-container">
<span class="switch-label">Pause on Hover</span>
<label class="switch">
<input type="checkbox" id="pause-on-hover">
<span class="slider"></span>
</label>
</div>
</div>
</div>
</section>
</div>
</div>
<div class="notification" id="notification"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
let pulseConfig = {
size: 8,
color: '#22c55e',
speed: 1.5,
scale: 2,
opacity: 100,
animationStyle: 'fade',
timingFunction: 'cubic-bezier(0.4, 0, 0.6, 1)',
shadowEnabled: true,
shadowBlur: 10,
spacing: 8,
pauseOnHover: false
};
const defaultConfig = { ...pulseConfig };
let currentPulseElement = null;
function generateUniqueId() {
return Math.random().toString(36).substring(2, 8);
}
function initStatusPulse() {
const elements = document.querySelectorAll('[data-pulse-dot]:not([data-pulse-initialized="true"])');
elements.forEach(element => {
createPulseElement(element);
element.dataset.pulseInitialized = 'true';
});
}
function createPulseElement(element) {
const dot = document.createElement('span');
dot.className = 'status-pulse-dot';
const opacity = pulseConfig.opacity / 100;
dot.style.cssText = `
position: relative;
display: inline-block;
width: ${pulseConfig.size}px;
height: ${pulseConfig.size}px;
background: ${pulseConfig.color};
border-radius: 50%;
margin-right: ${pulseConfig.spacing}px;
vertical-align: middle;
opacity: ${opacity};
${pulseConfig.shadowEnabled ? `box-shadow: 0 0 ${pulseConfig.shadowBlur}px ${pulseConfig.color};` : ''}
`;
createPulseAnimation(dot);
if (pulseConfig.pauseOnHover) {
element.addEventListener('mouseenter', () => pauseAnimation(dot));
element.addEventListener('mouseleave', () => resumeAnimation(dot));
}
element.insertBefore(dot, element.firstChild);
element.style.display = 'inline-flex';
element.style.alignItems = 'center';
updateAnimationStyles();
if (element.id === 'pulse-preview') {
currentPulseElement = dot;
}
}
function createPulseAnimation(dot) {
if (pulseConfig.animationStyle === 'fade' || pulseConfig.animationStyle === 'double') {
const pulseCount = pulseConfig.animationStyle === 'double' ? 2 : 1;
for (let i = 0; i < pulseCount; i++) {
const pulse = document.createElement('span');
pulse.style.cssText = `
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: inherit;
border-radius: inherit;
animation: pulseFade${pulseConfig.size} ${pulseConfig.speed}s ${pulseConfig.timingFunction} infinite;
${pulseConfig.animationStyle === 'double' && i === 1 ? `animation-delay: ${pulseConfig.speed / 2}s;` : ''}
`;
dot.appendChild(pulse);
}
} else if (pulseConfig.animationStyle === 'grow') {
dot.style.animation = `pulseGrow${pulseConfig.size} ${pulseConfig.speed}s ${pulseConfig.timingFunction} infinite`;
} else if (pulseConfig.animationStyle === 'both') {
const pulse = document.createElement('span');
pulse.style.cssText = `
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: inherit;
border-radius: inherit;
animation: pulseBoth${pulseConfig.size} ${pulseConfig.speed}s ${pulseConfig.timingFunction} infinite;
`;
dot.appendChild(pulse);
}
}
function pauseAnimation(dot) {
dot.style.animationPlayState = 'paused';
dot.querySelectorAll('span').forEach(span => {
span.style.animationPlayState = 'paused';
});
}
function resumeAnimation(dot) {
dot.style.animationPlayState = 'running';
dot.querySelectorAll('span').forEach(span => {
span.style.animationPlayState = 'running';
});
}
function updateAnimationStyles() {
document.querySelectorAll('.pulse-animation-style').forEach(el => el.remove());
const style = document.createElement('style');
style.className = 'pulse-animation-style';
let animations = '';
if (pulseConfig.animationStyle === 'fade' || pulseConfig.animationStyle === 'double') {
animations += `
@keyframes pulseFade${pulseConfig.size} {
0% { transform: scale(1); opacity: ${pulseConfig.opacity / 100}; }
100% { transform: scale(${pulseConfig.scale}); opacity: 0; }
}
`;
}
if (pulseConfig.animationStyle === 'grow') {
animations += `
@keyframes pulseGrow${pulseConfig.size} {
0%, 100% { transform: scale(1); }
50% { transform: scale(${pulseConfig.scale}); }
}
`;
}
if (pulseConfig.animationStyle === 'both') {
animations += `
@keyframes pulseBoth${pulseConfig.size} {
0% { transform: scale(1); opacity: ${pulseConfig.opacity / 100}; }
50% { transform: scale(${pulseConfig.scale}); opacity: ${pulseConfig.opacity / 200}; }
100% { transform: scale(1); opacity: ${pulseConfig.opacity / 100}; }
}
`;
}
style.textContent = animations;
document.head.appendChild(style);
}
function updateConfig() {
if (currentPulseElement) {
const opacity = pulseConfig.opacity / 100;
currentPulseElement.style.width = `${pulseConfig.size}px`;
currentPulseElement.style.height = `${pulseConfig.size}px`;
currentPulseElement.style.background = pulseConfig.color;
currentPulseElement.style.marginRight = `${pulseConfig.spacing}px`;
currentPulseElement.style.opacity = opacity;
currentPulseElement.style.boxShadow = pulseConfig.shadowEnabled ?
`0 0 ${pulseConfig.shadowBlur}px ${pulseConfig.color}` : '';
currentPulseElement.innerHTML = '';
createPulseAnimation(currentPulseElement);
updateAnimationStyles();
}
}
function updatePreview() {
document.querySelectorAll('.status-pulse-dot').forEach(el => el.remove());
document.querySelectorAll('[data-pulse-initialized]').forEach(el => {
el.removeAttribute('data-pulse-initialized');
});
initStatusPulse();
}
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) {
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;
} 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 showNotification(message, type = 'success') {
const notification = document.getElementById('notification');
notification.textContent = message;
notification.className = `notification ${type}`;
notification.offsetHeight;
notification.style.visibility = 'visible';
notification.classList.add('show');
setTimeout(() => {
notification.classList.remove('show');
setTimeout(() => {
if (!notification.classList.contains('show')) {
notification.style.visibility = 'hidden';
}
}, 400);
}, 3000);
}
function generateRandomColor() {
return '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0');
}
function generateFullSectionJSON() {
const sectionId = generateUniqueId();
const containerId = generateUniqueId();
const divId = generateUniqueId();
const textId = generateUniqueId();
const codeId = generateUniqueId();
const jsCode = generateJavaScriptCode();
const bricksJSON = {
"content": [
{
"id": sectionId,
"name": "section",
"parent": 0,
"children": [containerId, codeId],
"settings": {
"_height": "500",
"_justifyContent": "center"
}
},
{
"id": containerId,
"name": "container",
"parent": sectionId,
"children": [divId],
"settings": {
"_alignItems": "center",
"_overflow": "hidden"
}
},
{
"id": divId,
"name": "div",
"parent": containerId,
"children": [textId],
"settings": {
"_attributes": [
{
"id": "jvsdgw",
"name": "data-pulse-dot"
}
],
"_padding": {
"top": "10",
"left": "20",
"right": "20",
"bottom": "10"
},
"_border": {
"radius": {
"top": "15",
"right": "15",
"bottom": "15",
"left": "15"
},
"width": {
"top": "1",
"right": "1",
"bottom": "1",
"left": "1"
},
"style": "solid",
"color": {
"hex": "#e3e3e3"
}
}
}
},
{
"id": textId,
"name": "text-basic",
"parent": divId,
"children": [],
"settings": {
"text": "Status pulse",
"tag": "span",
"_typography": {
"color": {
"hex": "#000000"
}
}
}
},
{
"id": codeId,
"name": "code",
"parent": sectionId,
"children": [],
"settings": {
"javascriptCode": jsCode,
"executeCode": true,
"_display": "none"
},
"label": "Status pulse JS"
}
],
"source": "bricksCopiedElements",
"sourceUrl": "https://test.bricksfusion.com",
"version": "2.0.1",
"globalClasses": [],
"globalElements": []
};
return JSON.stringify(bricksJSON, null, 2);
}
function generateJavaScriptCode() {
return `function createPulseDot() {
const elements = document.querySelectorAll('[data-pulse-dot]:not([data-pulse-initialized])');
elements.forEach(element => {
element.setAttribute('data-pulse-initialized', 'true');
// Get custom attributes or use defaults
const size = element.getAttribute('data-pulse-size') || '${pulseConfig.size}';
const color = element.getAttribute('data-pulse-color') || '${pulseConfig.color}';
const speed = element.getAttribute('data-pulse-speed') || '${pulseConfig.speed}';
const scale = element.getAttribute('data-pulse-scale') || '${pulseConfig.scale}';
const opacityRaw = element.getAttribute('data-pulse-opacity') || '${pulseConfig.opacity}';
const opacity = parseInt(opacityRaw) / 100;
const spacing = element.getAttribute('data-pulse-spacing') || '${pulseConfig.spacing}';
const shadowEnabled = element.getAttribute('data-pulse-shadow-enabled') !== null ?
element.getAttribute('data-pulse-shadow-enabled') === 'true' : ${pulseConfig.shadowEnabled};
const shadowBlur = element.getAttribute('data-pulse-shadow-blur') || '${pulseConfig.shadowBlur}';
const animationStyle = element.getAttribute('data-pulse-animation-style') || '${pulseConfig.animationStyle}';
const timingFunction = element.getAttribute('data-pulse-timing-function') || '${pulseConfig.timingFunction}';
const pauseOnHover = element.getAttribute('data-pulse-pause-hover') !== null ?
element.getAttribute('data-pulse-pause-hover') === 'true' : ${pulseConfig.pauseOnHover};
// Create dot element
const dot = document.createElement('span');
dot.className = 'status-pulse-dot';
dot.style.cssText = \`
position: relative;
display: inline-block;
width: \${size}px;
height: \${size}px;
background: \${color};
border-radius: 50%;
margin-right: \${spacing}px;
vertical-align: middle;
opacity: \${opacity};
\${shadowEnabled ? \`box-shadow: 0 0 \${shadowBlur}px \${color};\` : ''}
\`;
// Generate unique animation name
const animationId = 'pulse_' + Math.random().toString(36).substr(2, 9);
// Create animations based on style
let keyframes = '';
if (animationStyle === 'fade') {
keyframes = \`
@keyframes \${animationId} {
0% { transform: scale(1); opacity: \${opacity}; }
100% { transform: scale(\${scale}); opacity: 0; }
}
\`;
const pulse = document.createElement('span');
pulse.style.cssText = \`
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: inherit;
border-radius: inherit;
animation: \${animationId} \${speed}s \${timingFunction} infinite;
\`;
dot.appendChild(pulse);
} else if (animationStyle === 'grow') {
keyframes = \`
@keyframes \${animationId} {
0%, 100% { transform: scale(1); }
50% { transform: scale(\${scale}); }
}
\`;
dot.style.animation = \`\${animationId} \${speed}s \${timingFunction} infinite\`;
} else if (animationStyle === 'both') {
keyframes = \`
@keyframes \${animationId} {
0% { transform: scale(1); opacity: \${opacity}; }
50% { transform: scale(\${scale}); opacity: \${opacity / 2}; }
100% { transform: scale(1); opacity: \${opacity}; }
}
\`;
const pulse = document.createElement('span');
pulse.style.cssText = \`
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: inherit;
border-radius: inherit;
animation: \${animationId} \${speed}s \${timingFunction} infinite;
\`;
dot.appendChild(pulse);
} else if (animationStyle === 'double') {
keyframes = \`
@keyframes \${animationId} {
0% { transform: scale(1); opacity: \${opacity}; }
100% { transform: scale(\${scale}); opacity: 0; }
}
\`;
for (let i = 0; i < 2; i++) {
const pulse = document.createElement('span');
pulse.style.cssText = \`
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: inherit;
border-radius: inherit;
animation: \${animationId} \${speed}s \${timingFunction} infinite;
\${i === 1 ? \`animation-delay: \${parseFloat(speed) / 2}s;\` : ''}
\`;
dot.appendChild(pulse);
}
}
// Inject keyframes
if (keyframes) {
const style = document.createElement('style');
style.textContent = keyframes;
document.head.appendChild(style);
}
// Add pause on hover functionality
if (pauseOnHover) {
element.addEventListener('mouseenter', () => {
dot.style.animationPlayState = 'paused';
dot.querySelectorAll('span').forEach(span => {
span.style.animationPlayState = 'paused';
});
});
element.addEventListener('mouseleave', () => {
dot.style.animationPlayState = 'running';
dot.querySelectorAll('span').forEach(span => {
span.style.animationPlayState = 'running';
});
});
}
// Insert dot and setup element display
element.insertBefore(dot, element.firstChild);
element.style.display = 'inline-flex';
element.style.alignItems = 'center';
});
}
// Initialize on DOM ready or immediately if already loaded
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', createPulseDot);
} else {
createPulseDot();
}
// Also run on dynamic content changes (optional)
const observer = new MutationObserver(() => {
createPulseDot();
});
observer.observe(document.body, {
childList: true,
subtree: true
});`;
}
function copyJsToClipboard() {
const jsCode = generateJavaScriptCode();
navigator.clipboard.writeText(jsCode)
.then(() => {
showNotification('JavaScript code copied to clipboard!');
})
.catch(err => {
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();
if (sectionJSON) {
navigator.clipboard.writeText(sectionJSON)
.then(() => {
showNotification('Full section JSON copied to clipboard!');
})
.catch(err => {
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');
}
});
} else {
showNotification('Failed to generate section JSON', 'error');
}
}
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 'pulse-size':
pulseConfig.size = defaultValue;
break;
case 'pulse-opacity':
pulseConfig.opacity = defaultValue;
break;
case 'pulse-speed':
pulseConfig.speed = defaultValue;
break;
case 'pulse-scale':
pulseConfig.scale = defaultValue;
break;
case 'shadow-blur':
pulseConfig.shadowBlur = defaultValue;
break;
case 'spacing':
pulseConfig.spacing = defaultValue;
break;
}
updateConfig();
showNotification(`${parameterId.replace(/-/g, ' ')} reset to default`);
}
};
function generateRandomPulse() {
const randomColors = [generateRandomColor()];
pulseConfig.color = randomColors[0];
pulseConfig.size = Math.floor(Math.random() * (20 - 4) + 4);
pulseConfig.speed = Math.round((Math.random() * (3 - 0.5) + 0.5) * 10) / 10;
pulseConfig.scale = Math.round((Math.random() * (3 - 1.5) + 1.5) * 10) / 10;
document.getElementById('pulse-color').value = pulseConfig.color;
document.getElementById('pulse-color-hex').value = pulseConfig.color;
document.getElementById('pulse-color-hsl').value = hexToHsl(pulseConfig.color);
document.getElementById('pulse-size').value = pulseConfig.size;
document.getElementById('pulse-speed').value = pulseConfig.speed;
document.getElementById('pulse-scale').value = pulseConfig.scale;
document.getElementById('pulse-size-value').textContent = pulseConfig.size;
document.getElementById('pulse-speed-value').textContent = pulseConfig.speed;
document.getElementById('pulse-scale-value').textContent = pulseConfig.scale;
const container = document.getElementById('pulse-color').closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', pulseConfig.color);
}
updateConfig();
showNotification('Random pulse generated!');
}
function initializeUI() {
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-pulse-dot');
});
document.getElementById('download-config').addEventListener('click', () => {
copyJsToClipboard();
});
document.getElementById('copy-full-section').addEventListener('click', () => {
copyFullSectionToClipboard();
});
document.getElementById('randomize-pulse').addEventListener('click', () => {
generateRandomPulse();
});
document.getElementById('reset-appearance').addEventListener('click', () => {
pulseConfig.size = defaultConfig.size;
pulseConfig.color = defaultConfig.color;
pulseConfig.opacity = defaultConfig.opacity;
document.getElementById('pulse-size').value = defaultConfig.size;
document.getElementById('pulse-color').value = defaultConfig.color;
document.getElementById('pulse-color-hex').value = defaultConfig.color;
document.getElementById('pulse-color-hsl').value = hexToHsl(defaultConfig.color);
document.getElementById('pulse-opacity').value = defaultConfig.opacity;
document.getElementById('pulse-size-value').textContent = defaultConfig.size;
document.getElementById('pulse-opacity-value').textContent = defaultConfig.opacity;
const container = document.getElementById('pulse-color').closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', defaultConfig.color);
}
updateConfig();
showNotification('Appearance settings reset');
});
document.getElementById('reset-animation').addEventListener('click', () => {
pulseConfig.speed = defaultConfig.speed;
pulseConfig.scale = defaultConfig.scale;
pulseConfig.animationStyle = defaultConfig.animationStyle;
pulseConfig.timingFunction = defaultConfig.timingFunction;
document.getElementById('pulse-speed').value = defaultConfig.speed;
document.getElementById('pulse-scale').value = defaultConfig.scale;
document.getElementById('animation-style').value = defaultConfig.animationStyle;
document.getElementById('timing-function').value = defaultConfig.timingFunction;
document.getElementById('pulse-speed-value').textContent = defaultConfig.speed;
document.getElementById('pulse-scale-value').textContent = defaultConfig.scale;
updateConfig();
showNotification('Animation settings reset');
});
document.getElementById('reset-advanced').addEventListener('click', () => {
pulseConfig.shadowEnabled = defaultConfig.shadowEnabled;
pulseConfig.shadowBlur = defaultConfig.shadowBlur;
pulseConfig.spacing = defaultConfig.spacing;
pulseConfig.pauseOnHover = defaultConfig.pauseOnHover;
document.getElementById('shadow-enabled').checked = defaultConfig.shadowEnabled;
document.getElementById('shadow-blur').value = defaultConfig.shadowBlur;
document.getElementById('spacing').value = defaultConfig.spacing;
document.getElementById('pause-on-hover').checked = defaultConfig.pauseOnHover;
document.getElementById('shadow-blur-value').textContent = defaultConfig.shadowBlur;
document.getElementById('spacing-value').textContent = defaultConfig.spacing;
updateConfig();
showNotification('Advanced settings reset');
});
const colorInput = document.getElementById('pulse-color');
const hexInput = document.getElementById('pulse-color-hex');
const hslInput = document.getElementById('pulse-color-hsl');
const container = colorInput.closest('.color-picker-container');
hslInput.value = hexToHsl(colorInput.value);
if (container) {
container.style.setProperty('--current-color', colorInput.value);
}
colorInput.addEventListener('input', () => {
const color = colorInput.value;
hexInput.value = color;
hslInput.value = hexToHsl(color);
if (container) {
container.style.setProperty('--current-color', color);
}
pulseConfig.color = color;
updateConfig();
});
hexInput.addEventListener('input', (e) => {
let hex = e.target.value;
if (!hex.startsWith('#')) {
hex = '#' + hex;
}
if (hex.length > 7) {
hex = hex.substring(0, 7);
}
e.target.value = hex.toUpperCase();
if (isValidHex(hex)) {
colorInput.value = hex;
hslInput.value = hexToHsl(hex);
if (container) {
container.style.setProperty('--current-color', hex);
}
pulseConfig.color = hex;
e.target.classList.remove('invalid');
updateConfig();
} else {
e.target.classList.add('invalid');
}
});
hslInput.addEventListener('input', (e) => {
const hsl = e.target.value;
if (isValidHsl(hsl)) {
const hex = hslToHex(hsl);
if (hex) {
colorInput.value = hex;
hexInput.value = hex;
if (container) {
container.style.setProperty('--current-color', hex);
}
pulseConfig.color = hex;
e.target.classList.remove('invalid');
updateConfig();
return;
}
}
e.target.classList.add('invalid');
});
const rangeInputs = document.querySelectorAll('input[type="range"]');
rangeInputs.forEach(input => {
const 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 'pulse-size':
pulseConfig.size = parseInt(input.value);
break;
case 'pulse-opacity':
pulseConfig.opacity = parseInt(input.value);
break;
case 'pulse-speed':
pulseConfig.speed = parseFloat(input.value);
break;
case 'pulse-scale':
pulseConfig.scale = parseFloat(input.value);
break;
case 'shadow-blur':
pulseConfig.shadowBlur = parseInt(input.value);
break;
case 'spacing':
pulseConfig.spacing = parseInt(input.value);
break;
}
updateConfig();
});
});
document.getElementById('animation-style').addEventListener('change', function() {
pulseConfig.animationStyle = this.value;
updatePreview();
});
document.getElementById('timing-function').addEventListener('change', function() {
pulseConfig.timingFunction = this.value;
updatePreview();
});
document.getElementById('shadow-enabled').addEventListener('change', function() {
pulseConfig.shadowEnabled = this.checked;
updateConfig();
});
document.getElementById('pause-on-hover').addEventListener('change', function() {
pulseConfig.pauseOnHover = this.checked;
updatePreview();
});
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();
const downloadBtn = document.getElementById('download-config');
if (downloadBtn && downloadBtn.hasAttribute('data-protection-animation')) {
downloadBtn.click();
} else {
copyJsToClipboard();
}
break;
case 's':
e.preventDefault();
const fullSectionBtn = document.getElementById('copy-full-section');
if (fullSectionBtn && fullSectionBtn.hasAttribute('data-protection-animation')) {
fullSectionBtn.click();
} else {
copyFullSectionToClipboard();
}
break;
}
} else {
switch (e.key.toLowerCase()) {
case 'r':
generateRandomPulse();
break;
}
}
});
function saveConfiguration() {
try {
localStorage.setItem('bricksfusion-pulse-config', JSON.stringify(pulseConfig));
} catch (e) {
// Silently fail if localStorage is not available
}
}
function loadConfiguration() {
try {
const saved = localStorage.getItem('bricksfusion-pulse-config');
if (saved) {
const savedConfig = JSON.parse(saved);
Object.assign(pulseConfig, savedConfig);
document.getElementById('pulse-size').value = savedConfig.size;
document.getElementById('pulse-color').value = savedConfig.color;
document.getElementById('pulse-color-hex').value = savedConfig.color;
document.getElementById('pulse-color-hsl').value = hexToHsl(savedConfig.color);
document.getElementById('pulse-opacity').value = savedConfig.opacity;
document.getElementById('pulse-speed').value = savedConfig.speed;
document.getElementById('pulse-scale').value = savedConfig.scale;
document.getElementById('animation-style').value = savedConfig.animationStyle;
document.getElementById('timing-function').value = savedConfig.timingFunction;
document.getElementById('shadow-enabled').checked = savedConfig.shadowEnabled;
document.getElementById('shadow-blur').value = savedConfig.shadowBlur;
document.getElementById('spacing').value = savedConfig.spacing;
document.getElementById('pause-on-hover').checked = savedConfig.pauseOnHover;
document.getElementById('pulse-size-value').textContent = savedConfig.size;
document.getElementById('pulse-opacity-value').textContent = savedConfig.opacity;
document.getElementById('pulse-speed-value').textContent = savedConfig.speed;
document.getElementById('pulse-scale-value').textContent = savedConfig.scale;
document.getElementById('shadow-blur-value').textContent = savedConfig.shadowBlur;
document.getElementById('spacing-value').textContent = savedConfig.spacing;
const container = document.getElementById('pulse-color').closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', savedConfig.color);
}
updatePreview();
}
} catch (e) {
// Silently fail if localStorage is not available
}
}
const originalUpdateConfig = updateConfig;
updateConfig = function() {
originalUpdateConfig();
saveConfiguration();
};
const originalUpdatePreview = updatePreview;
updatePreview = function() {
originalUpdatePreview();
saveConfiguration();
};
loadConfiguration();
setTimeout(() => {
showNotification('✅ BricksFusion Status Pulse Configurator loaded! All parameters now work correctly.');
}, 500);
}
initStatusPulse();
initializeUI();
});
</script>
</body>
</html>
Status Pulse
Adds an animated pulsing dot to show live status indicators. Perfect for "Online", "Live", "Recording", or any real-time status display.
Appearance
Size of the pulsing dot. Smaller dots are subtle and professional, larger ones are more attention-grabbing.
Default: 8px
Color of the status dot. Green for online/active, red for live/recording, blue for processing, yellow for busy/away.
Default: Green (#22c55e)
How see-through the dot is. 100 is fully solid, lower values make it more transparent and subtle.
Default: 100%
Gap between the dot and your text. Adjust to match your design needs.
Default: 8px
Adds a soft glow around the dot that matches its color. Makes it more visible and eye-catching.
Default: On
Size of the glow effect. Higher values create a bigger, softer glow around the dot.
Default: 10px
Animation
Type of pulse animation. Fade expands and fades out, Grow scales the dot itself, Both combines fading with growing, Double creates two overlapping pulses.
Default: Fade
How fast the pulse animation cycles. Lower is quicker and more urgent, higher is slower and calmer.
Default: 1.5s
How much bigger the pulse grows. 1.0 means no growth, 2.0 doubles in size, 3.0 triples. Larger values are more dramatic.
Default: 2.0
Animation curve that controls the motion feel. Ease creates natural movement, Linear is constant speed, Custom lets you fine-tune with cubic-bezier.
Default: Ease (cubic-bezier(0.4, 0, 0.6, 1))
Behavior
Stops the pulse animation when someone hovers over it. Useful when you want the status to be readable without distraction.
Default: Off
Performance
This element is extremely lightweight and uses only CSS animations. You can use dozens of status pulses on a single page without any performance concerns. Perfect for user lists, notification systems, and real-time dashboards.