Implement three-layer button architecture for consistent formatting button styling

- Add button frame isolation layer to prevent site CSS from affecting toolbar appearance
- Create style sample container for authentic style previews without layout interference
- Update CSS with proper containment boundaries and !important rules for button structure
- Preserve all authentic styling (color, weight, transform, decoration) in isolated preview
- Fix inconsistent button appearance across different site stylesheets (e.g. .brand class)
- Maintain professional toolbar UX while showing accurate style previews
This commit is contained in:
2025-09-21 22:06:35 +02:00
parent 0cfce1c95a
commit 551b3fa301
3 changed files with 97 additions and 95 deletions

View File

@@ -370,62 +370,59 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after {
transform: translateY(1px); transform: translateY(1px);
} }
/* Style preview buttons - new approach using CSS isolation */ /* Style preview buttons - three-layer architecture for style isolation */
.insertr-style-btn.insertr-style-preview { .insertr-style-btn.insertr-style-preview {
/* Clean button container - minimal styling */ /* Strong button container - prevents content from affecting button structure */
background: var(--insertr-bg-primary); background: var(--insertr-bg-primary) !important;
border: 1px solid var(--insertr-border-color); border: 1px solid var(--insertr-border-color) !important;
border-radius: var(--insertr-border-radius); border-radius: var(--insertr-border-radius) !important;
padding: 0; /* Remove padding - let preview content handle it */ padding: 0 !important; /* Let frame handle padding */
font-size: var(--insertr-font-size-sm); font-size: var(--insertr-font-size-sm);
cursor: pointer; cursor: pointer;
transition: var(--insertr-transition); transition: var(--insertr-transition);
/* Button layout */ /* Fixed button dimensions */
min-height: 28px; min-height: 32px;
display: inline-flex; display: inline-flex;
align-items: center;
justify-content: center;
/* Reset any inherited text styles on the button container only */ /* Reset button-level text properties to prevent inheritance */
font-family: var(--insertr-font-family); font-family: var(--insertr-font-family);
text-decoration: none; text-decoration: none;
font-weight: normal; font-weight: normal;
text-transform: none; text-transform: none;
color: var(--insertr-text-primary);
}
/* Button frame - consistent internal layout immune to content styling */
.insertr-button-frame {
display: flex;
align-items: center;
justify-content: center;
padding: 6px 10px;
width: 100%;
/* Don't set color here - let the preview content inherit naturally */ /* Create style containment boundary */
contain: layout style;
/* Ensure content fits */
overflow: hidden; overflow: hidden;
} }
/* Preview content container - minimal interference with original styling */ /* Style sample - authentic style preview with NO resets */
.insertr-preview-content { .insertr-style-sample {
/* Allow the original classes to style this element completely naturally */ /* Authentic style preview - let all styling come through naturally */
display: inline-block; display: inline-block;
/* Only set essential layout properties */ /* Size constraints only */
box-sizing: border-box; max-width: 100px;
/* Ensure text fits within button */
white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
max-width: 120px; white-space: nowrap;
/* Inherit font size from button container */ /* All other styling (color, weight, transform, decoration, etc.)
font-size: inherit; comes from applied classes - no interference */
/* Remove browser defaults that might interfere - but don't override intentional styling */
border: none;
margin: 0;
/* NO background, color, padding defaults - let the classes handle everything */
} }
/* Minimal fallback styling when no meaningful classes are detected */ /* Fallback styling when no meaningful classes are detected */
.insertr-preview-content.insertr-fallback-style { .insertr-style-sample.insertr-fallback-style {
padding: var(--insertr-spacing-xs) var(--insertr-spacing-sm);
color: var(--insertr-text-primary); color: var(--insertr-text-primary);
} }
@@ -450,7 +447,7 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after {
box-shadow: 0 2px 4px rgba(0, 123, 255, 0.3); box-shadow: 0 2px 4px rgba(0, 123, 255, 0.3);
} }
.insertr-style-btn.insertr-style-active .insertr-preview-content { .insertr-style-btn.insertr-style-active .insertr-style-sample {
color: white; color: white;
} }
@@ -468,7 +465,7 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after {
box-shadow: 0 2px 4px rgba(23, 162, 184, 0.3); box-shadow: 0 2px 4px rgba(23, 162, 184, 0.3);
} }
.insertr-style-btn.insertr-default-style.insertr-style-active .insertr-preview-content { .insertr-style-btn.insertr-default-style.insertr-style-active .insertr-style-sample {
color: white; color: white;
} }

View File

@@ -370,62 +370,59 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after {
transform: translateY(1px); transform: translateY(1px);
} }
/* Style preview buttons - new approach using CSS isolation */ /* Style preview buttons - three-layer architecture for style isolation */
.insertr-style-btn.insertr-style-preview { .insertr-style-btn.insertr-style-preview {
/* Clean button container - minimal styling */ /* Strong button container - prevents content from affecting button structure */
background: var(--insertr-bg-primary); background: var(--insertr-bg-primary) !important;
border: 1px solid var(--insertr-border-color); border: 1px solid var(--insertr-border-color) !important;
border-radius: var(--insertr-border-radius); border-radius: var(--insertr-border-radius) !important;
padding: 0; /* Remove padding - let preview content handle it */ padding: 0 !important; /* Let frame handle padding */
font-size: var(--insertr-font-size-sm); font-size: var(--insertr-font-size-sm);
cursor: pointer; cursor: pointer;
transition: var(--insertr-transition); transition: var(--insertr-transition);
/* Button layout */ /* Fixed button dimensions */
min-height: 28px; min-height: 32px;
display: inline-flex; display: inline-flex;
align-items: center;
justify-content: center;
/* Reset any inherited text styles on the button container only */ /* Reset button-level text properties to prevent inheritance */
font-family: var(--insertr-font-family); font-family: var(--insertr-font-family);
text-decoration: none; text-decoration: none;
font-weight: normal; font-weight: normal;
text-transform: none; text-transform: none;
color: var(--insertr-text-primary);
}
/* Button frame - consistent internal layout immune to content styling */
.insertr-button-frame {
display: flex;
align-items: center;
justify-content: center;
padding: 6px 10px;
width: 100%;
/* Don't set color here - let the preview content inherit naturally */ /* Create style containment boundary */
contain: layout style;
/* Ensure content fits */
overflow: hidden; overflow: hidden;
} }
/* Preview content container - minimal interference with original styling */ /* Style sample - authentic style preview with NO resets */
.insertr-preview-content { .insertr-style-sample {
/* Allow the original classes to style this element completely naturally */ /* Authentic style preview - let all styling come through naturally */
display: inline-block; display: inline-block;
/* Only set essential layout properties */ /* Size constraints only */
box-sizing: border-box; max-width: 100px;
/* Ensure text fits within button */
white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
max-width: 120px; white-space: nowrap;
/* Inherit font size from button container */ /* All other styling (color, weight, transform, decoration, etc.)
font-size: inherit; comes from applied classes - no interference */
/* Remove browser defaults that might interfere - but don't override intentional styling */
border: none;
margin: 0;
/* NO background, color, padding defaults - let the classes handle everything */
} }
/* Minimal fallback styling when no meaningful classes are detected */ /* Fallback styling when no meaningful classes are detected */
.insertr-preview-content.insertr-fallback-style { .insertr-style-sample.insertr-fallback-style {
padding: var(--insertr-spacing-xs) var(--insertr-spacing-sm);
color: var(--insertr-text-primary); color: var(--insertr-text-primary);
} }
@@ -450,7 +447,7 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after {
box-shadow: 0 2px 4px rgba(0, 123, 255, 0.3); box-shadow: 0 2px 4px rgba(0, 123, 255, 0.3);
} }
.insertr-style-btn.insertr-style-active .insertr-preview-content { .insertr-style-btn.insertr-style-active .insertr-style-sample {
color: white; color: white;
} }
@@ -468,7 +465,7 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after {
box-shadow: 0 2px 4px rgba(23, 162, 184, 0.3); box-shadow: 0 2px 4px rgba(23, 162, 184, 0.3);
} }
.insertr-style-btn.insertr-default-style.insertr-style-active .insertr-preview-content { .insertr-style-btn.insertr-default-style.insertr-style-active .insertr-style-sample {
color: white; color: white;
} }

View File

@@ -597,27 +597,32 @@ export class StyleAwareEditor {
button.dataset.styleId = styleId; button.dataset.styleId = styleId;
} }
// Create preview content container // Create three-layer structure for style isolation
const previewContent = document.createElement('span'); const buttonFrame = document.createElement('span');
previewContent.className = 'insertr-preview-content'; buttonFrame.className = 'insertr-button-frame';
previewContent.textContent = text;
const styleSample = document.createElement('span');
styleSample.className = 'insertr-style-sample';
styleSample.textContent = text;
// Apply type-specific styling // Apply type-specific styling
switch (type) { switch (type) {
case 'style': case 'style':
this.applyStyleButtonStyling(button, previewContent, styleInfo); this.applyStyleButtonStyling(button, styleSample, styleInfo);
break; break;
case 'link': case 'link':
this.applyLinkButtonStyling(button, previewContent); this.applyLinkButtonStyling(button, styleSample);
break; break;
case 'action': case 'action':
default: default:
// Default action button styling // Default action button styling - add fallback class
styleSample.classList.add('insertr-fallback-style');
break; break;
} }
// Add the preview content to the button // Build three-layer structure: button → frame → sample
button.appendChild(previewContent); buttonFrame.appendChild(styleSample);
button.appendChild(buttonFrame);
// Add click handler // Add click handler
button.addEventListener('click', (e) => { button.addEventListener('click', (e) => {
@@ -631,48 +636,51 @@ export class StyleAwareEditor {
/** /**
* Apply styling for style buttons * Apply styling for style buttons
*/ */
applyStyleButtonStyling(button, previewContent, styleInfo) { applyStyleButtonStyling(button, styleSample, styleInfo) {
if (!styleInfo) return; if (!styleInfo) return;
// Apply styling based on whether this is a detected style or default // Apply styling based on whether this is a detected style or default
if (styleInfo.isDefault) { if (styleInfo.isDefault) {
// Default style - apply semantic styling // Default style - apply semantic styling
button.classList.add('insertr-default-style'); button.classList.add('insertr-default-style');
previewContent.classList.add('insertr-default-preview');
// Add semantic styling for default elements // Add semantic styling for default elements
if (styleInfo.tagName === 'strong') { if (styleInfo.tagName === 'strong') {
previewContent.style.fontWeight = 'bold'; styleSample.style.fontWeight = 'bold';
} else if (styleInfo.tagName === 'em') { } else if (styleInfo.tagName === 'em') {
previewContent.style.fontStyle = 'italic'; styleSample.style.fontStyle = 'italic';
} else if (styleInfo.tagName === 'a') { } else if (styleInfo.tagName === 'a') {
previewContent.style.textDecoration = 'underline'; styleSample.style.textDecoration = 'underline';
previewContent.style.color = '#0066cc'; styleSample.style.color = '#0066cc';
} }
// Add helpful description to title // Add helpful description to title
button.title = `${styleInfo.name}: ${styleInfo.description || 'Default formatting option'}`; button.title = `${styleInfo.name}: ${styleInfo.description || 'Default formatting option'}`;
} else if (styleInfo.element && styleInfo.classes && styleInfo.classes.length > 0) { } else if (styleInfo.element && styleInfo.classes && styleInfo.classes.length > 0) {
// Detected style - apply original classes to preview // Detected style - apply original classes to style sample
button.classList.add('insertr-style-preview'); button.classList.add('insertr-style-preview');
// Add the detected classes to the preview content // Add the detected classes to the style sample (isolated preview)
styleInfo.classes.forEach(className => { styleInfo.classes.forEach(className => {
previewContent.classList.add(className); styleSample.classList.add(className);
}); });
button.title = `Apply ${styleInfo.classes.join(' ')} styling`;
} else { } else {
// No meaningful styles detected - use fallback // No meaningful styles detected - use fallback
previewContent.classList.add('insertr-fallback-style'); button.classList.add('insertr-style-preview');
styleSample.classList.add('insertr-fallback-style');
} }
} }
/** /**
* Apply styling for link buttons * Apply styling for link buttons
*/ */
applyLinkButtonStyling(button, previewContent) { applyLinkButtonStyling(button, styleSample) {
// Add link-specific styling // Add link-specific styling to the isolated sample
previewContent.style.textDecoration = 'underline'; styleSample.style.textDecoration = 'underline';
previewContent.style.color = '#0066cc'; styleSample.style.color = '#0066cc';
button.title = 'Create hyperlink';
} }
/** /**