
glow-component-factory
by PROdotes
MP3 library manager and auto player
SKILL.md
name: Glow Component Factory description: Step-by-step guide for creating Industrial Amber UI components following the GlowFactory pattern.
Glow Component Factory Workflow
This skill guides the creation of new Industrial Amber design system components for Gosling2.
The Glow Component Philosophy
Core Principles:
- NO Inline Styles: All styling via
theme.qssandobjectName - Consistent Factory: Always extend GlowWidget base classes
- Amber Aesthetic: Dark background, amber accents, tactical readability
- Signal-Based: Components communicate via signals, not direct references
Component Creation Workflow
Phase 1: Design & Planning
Step 1.1: Define Component Purpose
Ask yourself:
- What is the component's primary function?
- Is it an input (button, line edit) or display (label, LED)?
- Does it need state (active/inactive, selected/unselected)?
- What signals does it need to emit?
Step 1.2: Check for Existing Components
Before creating new component, check:
src/presentation/widgets/glow/
__init__.py
base.py # GlowWidget base class
button.py # GlowButton
line_edit.py # GlowLineEdit
combo_box.py # GlowComboBox
led.py # GlowLED
toggle.py # GlowToggle
tooltip.py # GlowTooltip
Can you extend or compose existing components instead?
Step 1.3: Choose Base Class
- Interactive Elements: Extend GlowWidget
- Buttons: Extend GlowButton
- Input Fields: Extend QLineEdit or QTextEdit
- Display Elements: Extend QLabel or QFrame
Phase 2: Implementation
Step 2.1: Create Component File
Location: src/presentation/widgets/glow/{component_name}.py
Template:
from PyQt6.QtWidgets import QWidget
from PyQt6.QtCore import pyqtSignal
from .base import GlowWidget # If extending GlowWidget
class Glow{ComponentName}(QWidget): # Or extend GlowWidget
"""
Brief description of component's purpose.
Signals:
signal_name: When signal is emitted
"""
# Define signals
value_changed = pyqtSignal(object)
def __init__(self, parent=None):
super().__init__(parent)
self._init_properties()
self._init_ui()
self._connect_signals()
def _init_properties(self):
"""Initialize internal state"""
self._value = None
self._is_active = False
def _init_ui(self):
"""Create and layout child widgets"""
# Create widgets
# Set object names
# Create layouts
# Apply layouts
def _connect_signals(self):
"""Connect internal signals/slots"""
pass
# Public API
def set_value(self, value):
"""Set component value"""
self._value = value
self._update_ui()
self.value_changed.emit(value)
def get_value(self):
"""Get current value"""
return self._value
# Private helpers
def _update_ui(self):
"""Update visual state based on internal state"""
pass
Step 2.2: Set Object Names (for QSS styling)
def _init_ui(self):
# Main widget
self.setObjectName("GlowMyComponent")
# Child widgets
self.label = QLabel("Label")
self.label.setObjectName("GlowLabel")
self.input = QLineEdit()
self.input.setObjectName("GlowInput")
Step 2.3: Implement Glow Effects (if interactive)
For hover/active states:
def enterEvent(self, event):
"""Mouse enter - trigger glow"""
self.setProperty("glow", "active")
self.style().polish(self) # Reapply stylesheet
super().enterEvent(event)
def leaveEvent(self, event):
"""Mouse leave - remove glow"""
self.setProperty("glow", "inactive")
self.style().polish(self)
super().leaveEvent(event)
Phase 3: Styling (theme.qss)
Step 3.1: Add QSS Rules
Location: src/resources/theme.qss
Template:
/* Glow{ComponentName} - Brief description */
QWidget#GlowMyComponent {
background-color: #000000;
border: 1px solid #FFC66D;
border-radius: 4px;
padding: 8px;
}
QWidget#GlowMyComponent[glow="active"] {
border: 2px solid #FFC66D;
box-shadow: 0 0 12px rgba(255, 198, 109, 0.6);
}
QLabel#GlowLabel {
color: #FFC66D;
font-family: "Bahnschrift Condensed", sans-serif;
font-size: 11pt;
text-transform: uppercase;
}
Step 3.2: Color Palette Usage
Use Industrial Amber colors:
/* Amber - Primary accent */
color: #FFC66D;
border-color: #FFC66D;
/* Muted Amber - Secondary */
color: #FF8C00;
/* Magenta - Critical actions */
background-color: #FF00FF;
/* Cyan - Unprocessed data */
color: #00E5FF;
/* Red - Errors */
color: #FF4444;
/* Void - Backgrounds */
background-color: #000000;
Step 3.3: Typography
font-family: "Bahnschrift Condensed", "Segoe UI", sans-serif;
font-size: 11pt; /* Body text */
font-size: 14pt; /* Headers */
text-transform: uppercase; /* For buttons and labels */
letter-spacing: 1px; /* Tactical spacing */
Phase 4: Integration
Step 4.1: Export from Glow Module
Update src/presentation/widgets/glow/__init__.py:
from .button import GlowButton
from .line_edit import GlowLineEdit
from .my_component import GlowMyComponent # Add new component
__all__ = [
'GlowButton',
'GlowLineEdit',
'GlowMyComponent', # Export
]
Step 4.2: Use in Parent Widget
from ..widgets.glow import GlowMyComponent
class MyFeatureWidget(QWidget):
def _init_ui(self):
# Create component
self.my_component = GlowMyComponent()
self.my_component.value_changed.connect(self._on_value_changed)
# Add to layout
layout.addWidget(self.my_component)
Phase 5: Testing
Step 5.1: Visual Testing
- Run application
- Navigate to component
- Verify:
- Colors match Industrial Amber palette
- Glow effects work on hover
- Text is readable
- Component responds to interaction
Step 5.2: Unit Testing
Create tests/unit/presentation/widgets/glow/test_my_component.py:
import pytest
from PyQt6.QtCore import Qt
from src.presentation.widgets.glow import GlowMyComponent
def test_component_creation(qtbot):
"""Test component can be created"""
component = GlowMyComponent()
qtbot.addWidget(component)
assert component is not None
def test_value_changed_signal(qtbot):
"""Test signal emission on value change"""
component = GlowMyComponent()
qtbot.addWidget(component)
with qtbot.waitSignal(component.value_changed, timeout=1000):
component.set_value("test")
assert component.get_value() == "test"
def test_mouse_interaction(qtbot):
"""Test hover effects"""
component = GlowMyComponent()
qtbot.addWidget(component)
# Simulate hover
qtbot.mouseMove(component)
# Verify property changed (if applicable)
Step 5.3: Integration Testing
Test in actual UI context:
def test_component_in_parent_widget(qtbot):
"""Test component works within parent widget"""
parent = MyFeatureWidget()
qtbot.addWidget(parent)
# Interact with component
component = parent.my_component
component.set_value("test")
# Verify parent receives signal
assert parent.current_value == "test"
Common Component Patterns
Pattern 1: Stateful Toggle
class GlowToggle(GlowWidget):
toggled = pyqtSignal(bool)
def __init__(self):
super().__init__()
self._is_on = False
def mousePressEvent(self, event):
self._is_on = not self._is_on
self.toggled.emit(self._is_on)
self._update_visual_state()
Pattern 2: Value Display with Units
class GlowValueDisplay(QWidget):
def __init__(self, label: str, unit: str = ""):
super().__init__()
self._label = label
self._unit = unit
self._value = None
def set_value(self, value: float):
self._value = value
self._update_display()
def _update_display(self):
text = f"{self._label}: {self._value} {self._unit}"
self.label_widget.setText(text)
Pattern 3: Multi-State Indicator
class GlowStatusLED(GlowLED):
class State(Enum):
OFF = "#333333"
ACTIVE = "#FFC66D"
WARNING = "#FF8C00"
ERROR = "#FF4444"
def set_state(self, state: State):
self.setGlowColor(state.value)
self.setActive(state != State.OFF)
Industrial Amber Design Guidelines
Spacing
- Tight: 4px (within components)
- Normal: 8px (between related components)
- Wide: 16px (between sections)
Borders
- Standard: 1px solid
- Active: 2px solid with glow
- Separator: 7px solid black bar
Glow Effects
/* Standard glow */
box-shadow: 0 0 8px rgba(255, 198, 109, 0.4);
/* Strong glow (active state) */
box-shadow: 0 0 16px rgba(255, 198, 109, 0.8);
/* Pulsing glow (critical state) */
animation: glow-pulse 1.5s ease-in-out infinite;
@keyframes glow-pulse {
0%, 100% { box-shadow: 0 0 8px rgba(255, 198, 109, 0.4); }
50% { box-shadow: 0 0 20px rgba(255, 198, 109, 1.0); }
}
Text Treatment
- All Caps for Controls: "SAVE", "CANCEL", "APPLY"
- Title Case for Data: "Artist Name", "Song Title"
- Uppercase for Status: "READY", "PROCESSING", "ERROR"
Anti-Patterns to Avoid
❌ Inline Styles
# BAD
button.setStyleSheet("background-color: red;")
# GOOD
button.setObjectName("ErrorButton")
# Define #ErrorButton in theme.qss
❌ Raw PyQt Widgets
# BAD
button = QPushButton("Click Me")
# GOOD
from ..widgets.glow import GlowButton
button = GlowButton("CLICK ME")
❌ Hardcoded Colors
# BAD
painter.setPen(QColor(255, 198, 109))
# GOOD
painter.setPen(QColor("#FFC66D")) # Or define in constants
❌ Fixed Sizes
# BAD
widget.setFixedSize(200, 100)
# GOOD
widget.setMinimumSize(200, 100)
widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
Completion Checklist
Before considering component complete:
- ✅ Extends appropriate GlowWidget base
- ✅ Has clear, descriptive objectName
- ✅ All styling in theme.qss (no inline styles)
- ✅ Uses Industrial Amber color palette
- ✅ Emits appropriate signals
- ✅ Has docstring explaining purpose
- ✅ Exported from glow/init.py
- ✅ Has unit tests
- ✅ Manually tested in UI
- ✅ Follows naming conventions
Score
Total Score
Based on repository quality metrics
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
Reviews
Reviews coming soon