feat: Implement dynamic style preview buttons
- Add JavaScript-based style copying from original elements to toolbar buttons - Use getComputedStyle() to dynamically apply color, font-weight, text-decoration, text-transform - Preserve button clickability with protected backgrounds and hover states - Support any CSS framework/custom styles without hardcoded mappings - Add comprehensive documentation to TODO.md for future enhancements Examples: - 'Emphasis' button now shows red bold text (from .emph class) - 'Highlight' button displays with style preview while remaining clickable - 'Brand' button demonstrates text-transform and color changes This provides intuitive visual feedback so users immediately understand what each formatting button will do to their content.
This commit is contained in:
122
TODO.md
122
TODO.md
@@ -535,3 +535,125 @@ The HTTP server has been successfully implemented and is production-ready:
|
|||||||
**Priority**: Low - implement after core functionality is stable and enterprise customers request advanced permissions.
|
**Priority**: Low - implement after core functionality is stable and enterprise customers request advanced permissions.
|
||||||
|
|
||||||
**Design Principle**: Keep simple `.insertr-gate` as default, add optional complexity only when needed.
|
**Design Principle**: Keep simple `.insertr-gate` as default, add optional complexity only when needed.
|
||||||
|
|
||||||
|
## 🎨 **Style Preview Enhancement System** (Dec 2024)
|
||||||
|
|
||||||
|
### **✅ IMPLEMENTED: Basic Style Button Previews**
|
||||||
|
|
||||||
|
**Goal**: Make formatting buttons visually preview the styles they represent.
|
||||||
|
|
||||||
|
**Current Implementation**:
|
||||||
|
- ✅ **Dynamic Style Copying** - JavaScript reads computed styles from original elements
|
||||||
|
- ✅ **Selective Property Application** - Copies color, font-weight, text-decoration, text-transform
|
||||||
|
- ✅ **Button Structure Preservation** - Maintains clickable button appearance with backgrounds/borders
|
||||||
|
- ✅ **Cross-Platform Compatibility** - Uses `window.getComputedStyle()` for universal support
|
||||||
|
|
||||||
|
**Example Results**:
|
||||||
|
- "Emphasis" button displays with red bold text (from `.emph { color: #dc2626; font-weight: 700; }`)
|
||||||
|
- "Highlight" button shows preview styling while remaining clickable
|
||||||
|
- "Brand" button demonstrates text-transform and color changes
|
||||||
|
|
||||||
|
### **🔄 Future Style Preview Enhancements**
|
||||||
|
|
||||||
|
#### **🎯 Priority 1: Enhanced Style Support**
|
||||||
|
- [ ] **Background Colors** - Safe background preview with contrast protection
|
||||||
|
- [ ] **Border Styles** - Preview border styles while maintaining button structure
|
||||||
|
- [ ] **Typography Styles** - Font-family, font-size adjustments with readability limits
|
||||||
|
- [ ] **Advanced CSS Properties** - Text-shadow, letter-spacing, line-height previews
|
||||||
|
|
||||||
|
#### **🎯 Priority 2: Interactive Previews**
|
||||||
|
- [ ] **Hover State Previews** - Show hover effects in button previews
|
||||||
|
- [ ] **Animation Previews** - Preview CSS transitions and animations safely
|
||||||
|
- [ ] **Responsive Previews** - Show how styles adapt across screen sizes
|
||||||
|
- [ ] **Nested Style Previews** - Handle complex nested styling scenarios
|
||||||
|
|
||||||
|
#### **🎯 Priority 3: Advanced Preview Features**
|
||||||
|
- [ ] **Custom Style Creation** - Visual style picker with live preview
|
||||||
|
- [ ] **Style Inheritance Display** - Show which properties come from which classes
|
||||||
|
- [ ] **Accessibility Validation** - Ensure previews meet contrast and readability standards
|
||||||
|
- [ ] **Performance Optimization** - Cache computed styles, minimize recomputation
|
||||||
|
|
||||||
|
### **🔧 Technical Implementation Details**
|
||||||
|
|
||||||
|
**Current Approach** (JavaScript-based style copying):
|
||||||
|
```javascript
|
||||||
|
const computedStyle = window.getComputedStyle(styleInfo.element);
|
||||||
|
if (computedStyle.color && computedStyle.color !== 'rgb(0, 0, 0)') {
|
||||||
|
button.style.setProperty('color', computedStyle.color, 'important');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Benefits**:
|
||||||
|
- ✅ Works with any CSS framework or custom styles
|
||||||
|
- ✅ No hardcoded style mappings required
|
||||||
|
- ✅ Automatically adapts to theme changes
|
||||||
|
- ✅ Handles complex CSS specificity scenarios
|
||||||
|
|
||||||
|
**Limitations**:
|
||||||
|
- ⚠️ Limited to properties safe for button elements
|
||||||
|
- ⚠️ Background colors skipped to maintain readability
|
||||||
|
- ⚠️ Some complex styles may not preview accurately
|
||||||
|
- ⚠️ Performance impact on large style sets
|
||||||
|
|
||||||
|
### **🎨 Design Considerations**
|
||||||
|
|
||||||
|
**Readability vs Accuracy Trade-offs**:
|
||||||
|
- **Button Backgrounds**: Always use safe background to ensure clickability
|
||||||
|
- **Text Contrast**: Ensure sufficient contrast between text and button background
|
||||||
|
- **Property Selection**: Only preview properties that don't break button functionality
|
||||||
|
- **Fallback Handling**: Graceful degradation when style copying fails
|
||||||
|
|
||||||
|
**User Experience Principles**:
|
||||||
|
- **Immediate Recognition**: Users should instantly understand what each button does
|
||||||
|
- **Consistent Interaction**: Buttons should always feel clickable regardless of style
|
||||||
|
- **Visual Hierarchy**: Style previews shouldn't overpower the editor interface
|
||||||
|
- **Accessibility**: Maintain WCAG compliance for all preview combinations
|
||||||
|
|
||||||
|
### **🧪 Testing Strategy for Style Previews**
|
||||||
|
|
||||||
|
**Cross-Browser Compatibility**:
|
||||||
|
- Test `getComputedStyle()` behavior across major browsers
|
||||||
|
- Validate CSS custom property support and inheritance
|
||||||
|
- Check for inconsistencies in color space handling
|
||||||
|
|
||||||
|
**Edge Case Handling**:
|
||||||
|
- Very long class names or deeply nested selectors
|
||||||
|
- Conflicting CSS properties from multiple stylesheets
|
||||||
|
- High contrast or accessibility mode compatibility
|
||||||
|
- Performance with large numbers of detected styles
|
||||||
|
|
||||||
|
**Real-World Style Testing**:
|
||||||
|
- Test with popular CSS frameworks (Tailwind, Bootstrap, Bulma)
|
||||||
|
- Validate with complex design systems and component libraries
|
||||||
|
- Check compatibility with CSS-in-JS solutions
|
||||||
|
- Test with user-defined custom properties and themes
|
||||||
|
|
||||||
|
### **📊 Future Metrics and Analytics**
|
||||||
|
|
||||||
|
**Style Preview Usage Analytics**:
|
||||||
|
- Track which style previews are most commonly used
|
||||||
|
- Measure user engagement with preview vs non-preview buttons
|
||||||
|
- Identify styles that cause preview rendering issues
|
||||||
|
- Monitor performance impact of style computation
|
||||||
|
|
||||||
|
**User Experience Improvements**:
|
||||||
|
- A/B test preview accuracy vs simplified representations
|
||||||
|
- Test user comprehension of style previews vs text labels
|
||||||
|
- Measure editing efficiency with vs without previews
|
||||||
|
- Gather feedback on preview helpfulness for different user types
|
||||||
|
|
||||||
|
### **Integration with Future Features**
|
||||||
|
|
||||||
|
**Style Management System**:
|
||||||
|
- Style preview integration with custom style creation tools
|
||||||
|
- Preview integration with theme switching and dark mode
|
||||||
|
- Connection to style documentation and design system integration
|
||||||
|
- Preview role in collaborative editing and style consistency
|
||||||
|
|
||||||
|
**Performance Optimization**:
|
||||||
|
- Style preview caching for frequently used styles
|
||||||
|
- Lazy loading of preview computation for off-screen elements
|
||||||
|
- Optimization for mobile devices and slower browsers
|
||||||
|
- Integration with build-time style optimization
|
||||||
|
|
||||||
|
**Note**: Current implementation provides solid foundation for all future enhancements while maintaining the zero-configuration philosophy.
|
||||||
|
|||||||
@@ -380,6 +380,56 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after {
|
|||||||
transform: translateY(1px);
|
transform: translateY(1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Style preview buttons - styled dynamically via JavaScript */
|
||||||
|
.insertr-style-btn.insertr-style-preview {
|
||||||
|
/* Preserve button structure */
|
||||||
|
background: var(--insertr-bg-primary) !important;
|
||||||
|
border: 1px solid var(--insertr-border-color) !important;
|
||||||
|
border-radius: var(--insertr-border-radius) !important;
|
||||||
|
padding: var(--insertr-spacing-xs) var(--insertr-spacing-sm) !important;
|
||||||
|
font-size: var(--insertr-font-size-sm) !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
transition: var(--insertr-transition) !important;
|
||||||
|
|
||||||
|
/* Button layout */
|
||||||
|
min-height: 28px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
/* Ensure button remains clickable */
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
/* Styles will be applied dynamically via JavaScript */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add subtle background to preview buttons to ensure they remain clickable-looking */
|
||||||
|
.insertr-style-btn.insertr-style-preview::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--insertr-bg-primary);
|
||||||
|
opacity: 0.9;
|
||||||
|
border-radius: inherit;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover state for preview buttons */
|
||||||
|
.insertr-style-btn.insertr-style-preview:hover {
|
||||||
|
background: var(--insertr-bg-secondary) !important;
|
||||||
|
border-color: var(--insertr-text-muted) !important;
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Active state for preview buttons */
|
||||||
|
.insertr-style-btn.insertr-style-preview:active {
|
||||||
|
background: var(--insertr-border-color) !important;
|
||||||
|
transform: translateY(1px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Editor components */
|
/* Editor components */
|
||||||
.insertr-simple-editor,
|
.insertr-simple-editor,
|
||||||
.insertr-rich-editor,
|
.insertr-rich-editor,
|
||||||
|
|||||||
@@ -380,6 +380,56 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after {
|
|||||||
transform: translateY(1px);
|
transform: translateY(1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Style preview buttons - styled dynamically via JavaScript */
|
||||||
|
.insertr-style-btn.insertr-style-preview {
|
||||||
|
/* Preserve button structure */
|
||||||
|
background: var(--insertr-bg-primary) !important;
|
||||||
|
border: 1px solid var(--insertr-border-color) !important;
|
||||||
|
border-radius: var(--insertr-border-radius) !important;
|
||||||
|
padding: var(--insertr-spacing-xs) var(--insertr-spacing-sm) !important;
|
||||||
|
font-size: var(--insertr-font-size-sm) !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
transition: var(--insertr-transition) !important;
|
||||||
|
|
||||||
|
/* Button layout */
|
||||||
|
min-height: 28px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
/* Ensure button remains clickable */
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
/* Styles will be applied dynamically via JavaScript */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add subtle background to preview buttons to ensure they remain clickable-looking */
|
||||||
|
.insertr-style-btn.insertr-style-preview::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--insertr-bg-primary);
|
||||||
|
opacity: 0.9;
|
||||||
|
border-radius: inherit;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover state for preview buttons */
|
||||||
|
.insertr-style-btn.insertr-style-preview:hover {
|
||||||
|
background: var(--insertr-bg-secondary) !important;
|
||||||
|
border-color: var(--insertr-text-muted) !important;
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Active state for preview buttons */
|
||||||
|
.insertr-style-btn.insertr-style-preview:active {
|
||||||
|
background: var(--insertr-border-color) !important;
|
||||||
|
transform: translateY(1px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Editor components */
|
/* Editor components */
|
||||||
.insertr-simple-editor,
|
.insertr-simple-editor,
|
||||||
.insertr-rich-editor,
|
.insertr-rich-editor,
|
||||||
|
|||||||
@@ -246,6 +246,42 @@ export class StyleAwareEditor {
|
|||||||
button.title = `Apply ${styleInfo.name} style`;
|
button.title = `Apply ${styleInfo.name} style`;
|
||||||
button.dataset.styleId = styleId;
|
button.dataset.styleId = styleId;
|
||||||
|
|
||||||
|
// Apply preview styling by copying computed styles from the original element
|
||||||
|
if (styleInfo.element && styleInfo.classes && styleInfo.classes.length > 0) {
|
||||||
|
// Add the detected classes first
|
||||||
|
styleInfo.classes.forEach(className => {
|
||||||
|
button.classList.add(className);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add special button class
|
||||||
|
button.classList.add('insertr-style-preview');
|
||||||
|
|
||||||
|
// Copy specific style properties from the original element to ensure they show
|
||||||
|
const computedStyle = window.getComputedStyle(styleInfo.element);
|
||||||
|
|
||||||
|
// Copy color (most important for visual preview)
|
||||||
|
if (computedStyle.color && computedStyle.color !== 'rgb(0, 0, 0)') {
|
||||||
|
button.style.setProperty('color', computedStyle.color, 'important');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy font-weight
|
||||||
|
if (computedStyle.fontWeight && computedStyle.fontWeight !== '400') {
|
||||||
|
button.style.setProperty('font-weight', computedStyle.fontWeight, 'important');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy text-decoration (for underlines, etc.)
|
||||||
|
if (computedStyle.textDecoration && computedStyle.textDecoration !== 'none') {
|
||||||
|
button.style.setProperty('text-decoration', computedStyle.textDecoration, 'important');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy text-transform (for uppercase, etc.)
|
||||||
|
if (computedStyle.textTransform && computedStyle.textTransform !== 'none') {
|
||||||
|
button.style.setProperty('text-transform', computedStyle.textTransform, 'important');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't copy background-color to keep button appearance
|
||||||
|
}
|
||||||
|
|
||||||
// Add click handler for style application
|
// Add click handler for style application
|
||||||
button.addEventListener('click', (e) => {
|
button.addEventListener('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
Reference in New Issue
Block a user