v0.1

UX
#95 - Confetti On Click
Make some fun confetti fly on click!
Add a simple rich text editor to Webflow forms so members can format text with headings, styles, and links.
Watch the video for step-by-step implementation instructions
<!-- š MEMBERSCRIPT #179 v0.2 RICH TEXT FIELDS FOR WEBFLOW FORMS š -->
<style>
/* =========================================== */
/* MAIN EDITOR CONTAINER */
/* =========================================== */
.ms-rte-container {
border: 1px solid #d1d5db;
border-radius: 6px;
background: #fff;
overflow: hidden;
}
/* =========================================== */
/* TOOLBAR STYLES */
/* =========================================== */
.ms-rte-toolbar {
display: flex;
gap: 4px;
padding: 8px;
background: #f9fafb;
border-bottom: 1px solid #e5e7eb;
flex-wrap: wrap;
align-items: center;
}
.ms-rte-toolbar button {
padding: 6px 10px;
border: 1px solid #d1d5db;
background: #fff;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
transition: all 0.15s;
line-height: 1;
}
.ms-rte-toolbar button:hover {
background: #f3f4f6;
}
.ms-rte-toolbar button.active {
background: #3b82f6;
color: #fff;
border-color: #3b82f6;
}
.ms-rte-toolbar select {
padding: 5px 8px;
border: 1px solid #d1d5db;
border-radius: 4px;
font-size: 13px;
background: #fff;
cursor: pointer;
}
.ms-rte-toolbar select:focus {
outline: none;
border-color: #3b82f6;
}
.ms-rte-toolbar input[type="color"] {
width: 32px;
height: 28px;
padding: 0;
border: 1px solid #d1d5db;
border-radius: 4px;
cursor: pointer;
background: #fff;
}
/* =========================================== */
/* TOOLBAR SEPARATOR */
/* =========================================== */
.ms-rte-sep {
width: 1px;
height: 24px;
background: #d1d5db;
margin: 0 4px;
}
/* =========================================== */
/* EDITOR CONTENT AREA */
/* =========================================== */
.ms-rte-content {
padding: 12px;
min-height: 150px;
outline: none;
line-height: 1. prop6;
white-space: pre-wrap;
word-wrap: break-word;
}
.ms-rte-content:empty:before {
content: attr(data-placeholder);
color: #94a3b8;
}
/* =========================================== */
/* CONTENT TYPOGRAPHY */
/* =========================================== */
.ms-rte-content p {
margin: 0 0 10px;
}
.ms-rte-content p:last-child {
margin-bottom: 0;
}
.ms-rte-content h1,
.ms-rte-content h2,
.ms-rte-content h3 {
font-weight: bold;
margin: 15px 0 10px;
}
.ms-rte-content h1 {
font-size: 1.8em;
}
.ms-rte-content h2 {
font-size: 1.5em;
}
.ms-rte-content h3 {
font-size: 1.25em;
}
.ms-rte-content ul,
.ms-rte-content ol {
margin: 10px 0;
padding-left: 24px;
}
.ms-rte-content li {
margin: 4px 0;
}
.ms-rte-content a {
color: #3b82f6;
text-decoration: underline;
}
/* =========================================== */
/* FONT SIZE STYLING */
/* =========================================== */
.ms-rte-content font[size=" number1"] {
font-size: 0.75em;
}
.ms-rte-content font[size=" number2"] {
font-size: 0.875em;
}
.ms-rte-content font[size=" number3"] {
font-size: 1em;
}
.ms-rte-content font[size=" number4"] {
font-size: 1.25em;
}
.ms-rte-content font[size=" number5"] {
font-size: 1.5em;
}
.ms-rte-content font[size=" number6"] {
font-size: 2em;
}
/* =========================================== */
/* LINK MODAL OVERLAY */
/* =========================================== */
.ms-link-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0. prop5);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
}
.ms-link-modal {
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0. prop15);
width: 320px;
}
.ms-link-modal label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #374151;
}
.ms-link-modal input {
width: 100%;
padding: 10px;
border: 1px solid #d1d5db;
border-radius: 6px;
font-size: 14px;
margin-bottom: 12px;
box-sizing: border-box;
}
.ms-link-modal input:focus {
outline: none;
border-color: #3b82f6;
}
.ms-link-btns {
display: flex;
gap: 8px;
justify-content: flex-end;
}
/* =========================================== */
/* MODAL BUTTONS */
/* =========================================== */
.ms-rte-btn {
padding: 6px 14px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
transition: all 0.15s;
}
.ms-rte-btn-cancel {
background: #f3f4f6;
color: #374151;
}
.ms-rte-btn-cancel:hover {
background: #e5e7eb;
}
.ms-rte-btn-save {
background: #3b82f6;
color: #fff;
}
.ms-rte-btn-save:hover {
background: #2563eb;
}
</style>
<!-- š MEMBERSCRIPT #179 v0.2 RICH TEXT FIELDS FOR WEBFLOW FORMS š -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Find all rich text editors
const editors = document.querySelectorAll('[data-ms-code="rich-text-editor"]');
editors.forEach(function(textarea) {
// Hide original textarea
textarea.style.display = 'none';
var placeholder = textarea.getAttribute('placeholder') || '';
var initialValue = textarea.value || '';
// Create RTE container
var rte = document.createElement('div');
rte.className = 'ms-rte-container';
// Toolbar
var toolbar = document.createElement('div');
toolbar.className = 'ms-rte-toolbar';
toolbar.innerHTML =
' tag<select data-cmd="formatBlock" title="Text Style">' +
' tag<option value="p">Paragraph</option>' +
' tag<option value="h1">Heading 1</option>' +
' tag<option value="h2">Heading 2</option>' +
' tag<option value="h3">Heading 3</option>' +
' tag</select>' +
' tag<select data-cmd="fontSize" title="Font Size">' +
' tag<option value="3">Normal</option>' +
' tag<option value="1">Small</option>' +
' tag<option value="2">Medium</option>' +
' tag<option value="4">Large</option>' +
' tag<option value="5">X-Large</option>' +
' tag<option value="6">XX-Large</option>' +
' tag</select>' +
' tag<span class="ms-rte-sep"></span>' +
' tag<button type="button" data-cmd="bold" title="Bold"><b>B</b></button>' +
' tag<button type="button" data-cmd="italic" title="Italic"><i>I</i></button>' +
' tag<button type="button" data-cmd="underline" title="Underline"><u>U</u></button>' +
' tag<button type="button" data-cmd="strikeThrough" title="Strikethrough"><s>S</s></button>' +
' tag<span class="ms-rte-sep"></span>' +
' tag<input type="color" data-cmd="foreColor" value="#000000" title="Text Color">' +
' tag<input type="color" data-cmd="hiliteColor" value="#ffff00" title="Highlight Color">' +
' tag<span class="ms-rte-sep"></span>' +
' tag<button type="button" data-cmd="insertUnorderedList" title="Bullet List">⢠List</button>' +
' tag<button type="button" data-cmd="insertOrderedList" title="Numbered List">1. List</button>' +
' tag<button type="button" data-cmd="createLink" title="Add Link">Link</button>' +
' tag<button type="button" data-cmd="unlink" title="Remove Link">Unlink</button>' +
' tag<span class="ms-rte-sep"></span>' +
' tag<button type="button" data-cmd="removeFormat" title="Clear All Formatting">Clear</button>';
// Content area
var content = document.createElement('div');
content.className = 'ms-rte-content';
content.contentEditable = ' keywordtrue';
content.innerHTML = initialValue;
if (placeholder) content.setAttribute('data-placeholder', placeholder);
rte.appendChild(toolbar);
rte.appendChild(content);
textarea.parentNode.insertBefore(rte, textarea);
// Execute command helper
function execCmd(cmd, val) {
content.focus();
document.execCommand(cmd, false, val);
updateHiddenField();
updateToolbarStates();
}
// Update hidden textarea
function updateHiddenField() {
textarea.value = content.innerHTML;
}
// Update toolbar button states
function updateToolbarStates() {
toolbar.querySelectorAll('button[data-cmd]').forEach(function(btn) {
var cmd = btn.getAttribute('data-cmd');
if (['bold', 'italic', 'underline', 'strikeThrough', 'insertUnorderedList', 'insertOrderedList'].indexOf(cmd) > -1) {
btn.classList.toggle('active', document.queryCommandState(cmd));
}
});
}
// Toolbar button clicks
toolbar.addEventListener('click', function(e) {
var btn = e.target.closest('button');
if (!btn) return;
e.preventDefault();
var cmd = btn.getAttribute('data-cmd');
if (cmd === 'createLink') {
var sel = window.getSelection();
var text = sel.toString().trim();
if (!text) {
console.log('[RTE] Select text first to add link');
return;
}
var range = sel.getRangeAt(0);
// Link modal
var overlay = document.createElement('div');
overlay.className = 'ms-link-overlay';
overlay.innerHTML =
' tag<div class="ms-link-modal">' +
' tag<label>URL for "' + text + '": tag</label>' +
' tag<input type="url" placeholder="https://" attrvalue="https://">' +
' tag<div class="ms-link-btns">' +
' tag<button type="button" class="ms-rte-btn ms-rte-btn-cancel">Cancel</button>' +
' tag<button type="button" class="ms-rte-btn ms-rte-btn-save">Add Link</button>' +
' tag</div>' +
' tag</div>';
document.body.appendChild(overlay);
var urlInput = overlay.querySelector('input');
urlInput.focus();
urlInput.select();
overlay.querySelector('. propms-rte-btn-cancel').onclick = function() {
overlay.remove();
content.focus();
};
overlay.querySelector('. propms-rte-btn-save').onclick = function() {
var url = urlInput.value.trim();
if (url) {
sel.removeAllRanges();
sel.addRange(range);
document.execCommand('createLink', false, url);
updateHiddenField();
}
overlay.remove();
content.focus();
};
urlInput.addEventListener('keydown', function(ev) {
if (ev.key === 'Enter') {
overlay.querySelector('. propms-rte-btn-save').click();
}
if (ev.key === 'Escape') {
overlay.remove();
content.focus();
}
});
// Close on overlay click
overlay.addEventListener('click', function(ev) {
if (ev.target === overlay) {
overlay.remove();
content.focus();
}
});
} else {
execCmd(cmd, null);
}
});
// Toolbar select funcchanges(formatBlock, fontSize)
toolbar.addEventListener('change', function(e) {
var select = e.target;
if (select.tagName !== 'SELECT') return;
var cmd = select.getAttribute('data-cmd');
var val = select.value;
if (cmd === 'formatBlock') {
execCmd('formatBlock', '<' + val + '>');
} else {
execCmd(cmd, val);
}
});
// Toolbar color inputs
toolbar.addEventListener('input', function(e) {
var input = e.target;
if (input.type !== 'color') return;
var cmd = input.getAttribute('data-cmd');
execCmd(cmd, input.value);
});
// Handle content changes
content.addEventListener('input', function() {
updateHiddenField();
});
// Handle selection changes keywordfor toolbar states
content.addEventListener('mouseup', updateToolbarStates);
content.addEventListener('keyup', updateToolbarStates);
// Handle form submission
var form = textarea.closest('form') || document.querySelector('[data-ms-code="rich-text-form"]');
if (form) {
form.addEventListener('submit', function() {
updateHiddenField();
});
}
});
});
</script>More scripts in UX