From 01c8dcca765e765f66b6421e22aa8b9678ed2e29 Mon Sep 17 00:00:00 2001 From: Joakim Date: Fri, 19 Sep 2025 20:48:01 +0200 Subject: [PATCH] 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. --- TODO.md | 122 ++++++++++++++++++++++++++++ internal/content/assets/insertr.css | 50 ++++++++++++ lib/src/styles/insertr.css | 50 ++++++++++++ lib/src/ui/style-aware-editor.js | 36 ++++++++ 4 files changed, 258 insertions(+) diff --git a/TODO.md b/TODO.md index 4365f7f..ee18325 100644 --- a/TODO.md +++ b/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. **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. diff --git a/internal/content/assets/insertr.css b/internal/content/assets/insertr.css index 4862303..e3e5f68 100644 --- a/internal/content/assets/insertr.css +++ b/internal/content/assets/insertr.css @@ -380,6 +380,56 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after { 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 */ .insertr-simple-editor, .insertr-rich-editor, diff --git a/lib/src/styles/insertr.css b/lib/src/styles/insertr.css index 4862303..e3e5f68 100644 --- a/lib/src/styles/insertr.css +++ b/lib/src/styles/insertr.css @@ -380,6 +380,56 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after { 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 */ .insertr-simple-editor, .insertr-rich-editor, diff --git a/lib/src/ui/style-aware-editor.js b/lib/src/ui/style-aware-editor.js index 2620a0a..9eec79e 100644 --- a/lib/src/ui/style-aware-editor.js +++ b/lib/src/ui/style-aware-editor.js @@ -246,6 +246,42 @@ export class StyleAwareEditor { button.title = `Apply ${styleInfo.name} style`; 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 button.addEventListener('click', (e) => { e.preventDefault();