9a848e8c53
Implement a command palette accessible via CMD+K/Ctrl+K using the ninja-keys web component. Features include: - New /api/cmd-k endpoint serving dynamic CV entries (experiences, projects, courses) - Language-aware responses with 1-hour cache headers - Scroll-to-section functionality for quick navigation - Enhanced keyboard shortcuts modal with CMD+K documentation - Comprehensive test coverage for API and UI interactions Also includes cleanup of deprecated debug test files and various UI polish improvements to contact form, themes, and action bar components.
393 lines
8.6 KiB
CSS
393 lines
8.6 KiB
CSS
/* =============================================================================
|
|
CONTACT FORM - Modal form styling
|
|
============================================================================= */
|
|
|
|
/* Utility class to hide elements after form submission */
|
|
.hidden {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Contact Modal Specific Overrides */
|
|
#contact-modal {
|
|
max-width: 520px;
|
|
}
|
|
|
|
#contact-modal .info-modal-cv-title {
|
|
color: #3498db; /* Blue subtitle for contact */
|
|
}
|
|
|
|
#contact-modal .info-modal-cv-title iconify-icon {
|
|
color: #3498db;
|
|
}
|
|
|
|
/* Contact Modal Description */
|
|
.contact-modal-description {
|
|
font-size: 0.95rem;
|
|
line-height: 1.6;
|
|
margin-bottom: 1.5rem;
|
|
color: #555;
|
|
text-align: center;
|
|
}
|
|
|
|
/* =============================================================================
|
|
FORM ELEMENTS
|
|
============================================================================= */
|
|
|
|
/* Form Group - Wrapper for label + input */
|
|
.form-group {
|
|
margin-bottom: 1.25rem;
|
|
}
|
|
|
|
.form-group:last-of-type {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
/* Form Labels */
|
|
.form-label {
|
|
display: block;
|
|
font-size: 0.9rem;
|
|
font-weight: 600;
|
|
color: #333;
|
|
margin-bottom: 0.4rem;
|
|
}
|
|
|
|
.required-indicator {
|
|
color: #ef4444;
|
|
margin-left: 0.2rem;
|
|
}
|
|
|
|
/* Form Inputs and Textarea */
|
|
.form-input,
|
|
.form-textarea {
|
|
width: 100%;
|
|
padding: 0.75rem;
|
|
font-size: 0.95rem;
|
|
font-family: inherit;
|
|
border: 2px solid #e0e0e0;
|
|
border-radius: 8px;
|
|
background: #ffffff;
|
|
color: #333;
|
|
transition: all 0.2s ease;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.form-input:focus,
|
|
.form-textarea:focus {
|
|
outline: none;
|
|
border-color: #3498db;
|
|
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
|
|
}
|
|
|
|
.form-input::placeholder,
|
|
.form-textarea::placeholder {
|
|
color: #999;
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Textarea specific */
|
|
.form-textarea {
|
|
resize: vertical;
|
|
min-height: 120px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
/* Invalid state (HTML5 validation) */
|
|
.form-input:invalid:not(:placeholder-shown),
|
|
.form-textarea:invalid:not(:placeholder-shown) {
|
|
border-color: #ef4444;
|
|
}
|
|
|
|
.form-input:invalid:focus:not(:placeholder-shown),
|
|
.form-textarea:invalid:focus:not(:placeholder-shown) {
|
|
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
|
|
}
|
|
|
|
/* =============================================================================
|
|
CONTACT RESPONSE MESSAGES
|
|
============================================================================= */
|
|
|
|
.contact-response {
|
|
margin-bottom: 1rem;
|
|
min-height: 0;
|
|
}
|
|
|
|
.contact-message {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 0.75rem;
|
|
padding: 1rem;
|
|
border-radius: 8px;
|
|
margin-bottom: 1rem;
|
|
animation: messageSlideIn 0.3s ease;
|
|
}
|
|
|
|
@keyframes messageSlideIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(-10px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
.contact-message iconify-icon {
|
|
flex-shrink: 0;
|
|
margin-top: 0.1rem;
|
|
}
|
|
|
|
.contact-message-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.contact-message-content strong {
|
|
display: block;
|
|
font-size: 0.95rem;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.contact-message-content p {
|
|
font-size: 0.875rem;
|
|
margin: 0;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
/* Success Message */
|
|
.contact-success {
|
|
background: linear-gradient(135deg, rgba(40, 167, 69, 0.1) 0%, rgba(25, 135, 84, 0.05) 100%);
|
|
border: 2px solid rgba(40, 167, 69, 0.3);
|
|
color: #155724;
|
|
}
|
|
|
|
.contact-success iconify-icon {
|
|
color: #28a745;
|
|
}
|
|
|
|
/* Error Message */
|
|
.contact-error {
|
|
background: linear-gradient(135deg, rgba(220, 53, 69, 0.1) 0%, rgba(200, 35, 51, 0.05) 100%);
|
|
border: 2px solid rgba(220, 53, 69, 0.3);
|
|
color: #721c24;
|
|
}
|
|
|
|
.contact-error iconify-icon {
|
|
color: #dc3545;
|
|
}
|
|
|
|
/* =============================================================================
|
|
FORM ACTIONS (Submit Button)
|
|
============================================================================= */
|
|
|
|
.form-actions {
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.contact-submit-btn {
|
|
width: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.5rem;
|
|
padding: 0.875rem 1.5rem;
|
|
font-size: 1rem;
|
|
font-weight: 600;
|
|
font-family: inherit;
|
|
border: none;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
|
|
color: white;
|
|
transition: all 0.2s ease;
|
|
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.3);
|
|
}
|
|
|
|
.contact-submit-btn:hover {
|
|
background: linear-gradient(135deg, #2980b9 0%, #3498db 100%);
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 6px 16px rgba(52, 152, 219, 0.4);
|
|
}
|
|
|
|
.contact-submit-btn:active {
|
|
transform: translateY(0);
|
|
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.3);
|
|
}
|
|
|
|
.contact-submit-btn:disabled {
|
|
background: #e0e0e0;
|
|
color: #999;
|
|
cursor: not-allowed;
|
|
transform: none;
|
|
box-shadow: none;
|
|
}
|
|
|
|
/* Loading spinner in button */
|
|
.contact-submit-btn .htmx-indicator {
|
|
display: none;
|
|
}
|
|
|
|
.contact-submit-btn.htmx-request .htmx-indicator {
|
|
display: inline-flex;
|
|
}
|
|
|
|
.contact-submit-btn.htmx-request > span {
|
|
opacity: 0.7;
|
|
}
|
|
|
|
/* Spinning animation */
|
|
.spinning {
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
/* =============================================================================
|
|
FORM NOTE
|
|
============================================================================= */
|
|
|
|
.form-note {
|
|
font-size: 0.8rem;
|
|
color: #666;
|
|
text-align: center;
|
|
margin: 0;
|
|
font-style: italic;
|
|
}
|
|
|
|
/* =============================================================================
|
|
CONTACT BUTTON (Fixed Position)
|
|
============================================================================= */
|
|
|
|
/* Contact Button - positioned above theme switcher (FIXED sidebar button only) */
|
|
.fixed-btn.contact-btn {
|
|
position: fixed !important; /* Override .has-tooltip position: relative */
|
|
bottom: 18rem; /* Above theme switcher (14rem) */
|
|
left: 2rem;
|
|
width: 50px;
|
|
height: 50px;
|
|
background: var(--black-bar, #2b2b2b);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 50%;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
transition: all 0.3s ease;
|
|
z-index: 999;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.fixed-btn.contact-btn:hover {
|
|
opacity: 1;
|
|
transform: translateY(-3px);
|
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
|
|
background: #3498db; /* Blue hover */
|
|
}
|
|
|
|
.fixed-btn.contact-btn.at-bottom {
|
|
opacity: 1;
|
|
background: #3498db !important; /* Blue when at bottom */
|
|
}
|
|
|
|
/* =============================================================================
|
|
RESPONSIVE DESIGN - CONTACT FORM
|
|
============================================================================= */
|
|
|
|
/* Mobile adjustments */
|
|
@media (max-width: 768px) {
|
|
#contact-modal {
|
|
max-width: calc(100vw - 2rem) !important;
|
|
width: calc(100vw - 2rem) !important;
|
|
max-height: calc(100vh - 2rem) !important;
|
|
}
|
|
|
|
#contact-modal .info-modal-content {
|
|
padding: 1.5rem 1rem;
|
|
max-height: calc(100vh - 2rem);
|
|
overflow-y: auto;
|
|
}
|
|
|
|
#contact-modal .info-modal-header h2 {
|
|
font-size: 1.25rem;
|
|
}
|
|
|
|
#contact-modal .info-modal-cv-title {
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.contact-modal-description {
|
|
font-size: 0.875rem;
|
|
margin-bottom: 1.25rem;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.form-label {
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.form-input,
|
|
.form-textarea {
|
|
font-size: 0.9rem;
|
|
padding: 0.65rem;
|
|
}
|
|
|
|
.contact-submit-btn {
|
|
font-size: 0.95rem;
|
|
padding: 0.75rem 1.25rem;
|
|
}
|
|
|
|
.form-note {
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
/* Contact button mobile (FIXED sidebar button only) */
|
|
.fixed-btn.contact-btn {
|
|
bottom: 13.5rem; /* Adjust for mobile button spacing */
|
|
left: 1.5rem;
|
|
width: 45px;
|
|
height: 45px;
|
|
}
|
|
}
|
|
|
|
/* =============================================================================
|
|
ACCESSIBILITY - REDUCED MOTION
|
|
============================================================================= */
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.contact-message {
|
|
animation: none;
|
|
}
|
|
|
|
.spinning {
|
|
animation: none;
|
|
}
|
|
|
|
.contact-submit-btn {
|
|
transition: none;
|
|
}
|
|
|
|
.form-input,
|
|
.form-textarea {
|
|
transition: none;
|
|
}
|
|
}
|
|
|
|
/* =============================================================================
|
|
PRINT - HIDE CONTACT ELEMENTS
|
|
============================================================================= */
|
|
|
|
@media print {
|
|
.contact-btn,
|
|
#contact-modal {
|
|
display: none !important;
|
|
}
|
|
}
|