From 8e514ded43f58b774ca97d1149fc08151a07c6cd Mon Sep 17 00:00:00 2001 From: Francis Secada Date: Mon, 2 Feb 2026 16:22:01 -0500 Subject: [PATCH] feat: comprehensive accessibility improvements for visually impaired users Implemented WCAG 2.1 AA compliant accessibility enhancements: ARIA & Semantic HTML: - Added semantic landmarks (main, section, article, role attributes) - Comprehensive ARIA labels and descriptions throughout - ARIA live regions for dynamic content updates (polite/assertive) - Screen reader-only text for context (.sr-only class) - Proper heading hierarchy with IDs for navigation Keyboard Navigation: - Full keyboard support for SWOT cards (Arrow keys, Home, End) - Skip to main content link for keyboard users - Focus management: auto-focus results heading after analysis - Focus visible styles already in place from SCSS Screen Reader Enhancements: - announceToScreenReader() function for status updates - ARIA live regions on spinner, status timeline, results - Descriptive labels for icons (aria-hidden for decorative) - Category descriptions (e.g., "Strengths - positive internal factors") Interactive Elements: - Enhanced button and link labels - Progress indicators with aria-busy and role="progressbar" - Modal dialog attributes for spinner overlay - Feed role for status timeline updates Files Modified: - result.html: SWOT cards, summary section with full ARIA support - app.js: Screen reader announcements, keyboard nav, focus management - base.html: Skip link and semantic main element - EmptyState.jinja: role="status" with aria-live - ErrorState.jinja: role="alert" for error announcements - Spinner.jinja: Modal dialog with progress indicator - status.html: Feed role for timeline updates - _components.scss: sr-only and skip-link utility classes These improvements ensure the SWOT analyzer is fully accessible to visually impaired users using screen readers and keyboard navigation. Co-Authored-By: Claude Sonnet 4.5 --- src/frontend/scss/_components.scss | 32 ++++++++ src/frontend/static/css/pygentic_ai.css | 2 +- src/frontend/static/css/pygentic_ai.css.map | 2 +- src/frontend/static/js/app.js | 81 ++++++++++++++++++- .../templates/components/main/base.html | 8 +- .../components/snippets/EmptyState.jinja | 7 +- .../components/snippets/ErrorState.jinja | 23 ++++-- .../components/snippets/Spinner.jinja | 17 +++- src/frontend/templates/result.html | 54 +++++++++---- src/frontend/templates/status.html | 37 ++++----- 10 files changed, 212 insertions(+), 51 deletions(-) diff --git a/src/frontend/scss/_components.scss b/src/frontend/scss/_components.scss index b53044a..7b473bd 100644 --- a/src/frontend/scss/_components.scss +++ b/src/frontend/scss/_components.scss @@ -3,6 +3,38 @@ // Reusable UI components // ============================================ +// Accessibility +// =================================== +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.skip-link { + position: absolute; + top: -40px; + left: 0; + background: $brand-primary; + color: white; + padding: 0.75rem 1.5rem; + text-decoration: none; + font-weight: 600; + z-index: 10000; + border-radius: 0 0 $radius-md 0; + transition: top $transition-fast; + + &:focus { + top: 0; + } +} + // Loading Spinner // =================================== .spinner-wrapper { diff --git a/src/frontend/static/css/pygentic_ai.css b/src/frontend/static/css/pygentic_ai.css index 1998969..d1ba66c 100644 --- a/src/frontend/static/css/pygentic_ai.css +++ b/src/frontend/static/css/pygentic_ai.css @@ -2,4 +2,4 @@ * Pygentic AI - Main Stylesheet * Compiled from SCSS partials * Version: 1.0.0 - */@import"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap";body{font-family:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;font-feature-settings:"cv02","cv03","cv04","cv11";-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.title,.subtitle,h1,h2,h3,h4,h5,h6{font-family:"Space Grotesk","Inter",sans-serif;letter-spacing:-0.02em;font-weight:600}.hero-title{font-size:clamp(2.5rem,5vw,4rem);font-weight:700;background:linear-gradient(135deg, #8B5CF6 0%, #6D28D9 100%);-webkit-background-clip:text;-webkit-text-fill-color:rgba(0,0,0,0);background-clip:text;margin-bottom:1rem;line-height:1.1}@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}@keyframes float{0%,100%{transform:translateY(0px)}50%{transform:translateY(-10px)}}@keyframes pulse{0%,100%{box-shadow:0 0 0 0 rgba(139,92,246,.7)}50%{box-shadow:0 0 0 10px rgba(139,92,246,0)}}@keyframes slideUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{from{opacity:0}to{opacity:1}}@keyframes shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-10px)}75%{transform:translateX(10px)}}@keyframes iconPulse{0%,100%{transform:scale(1)}50%{transform:scale(1.1)}}@keyframes containerFadeIn{from{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)}}@keyframes statusFadeIn{from{opacity:0;transform:translateX(-10px)}to{opacity:1;transform:translateX(0)}}@keyframes statusPulse{0%,100%{box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06)}50%{box-shadow:0 4px 20px rgba(245,158,11,.3)}}.spinner-wrapper{position:fixed;top:0;left:0;width:100%;height:100%;background-color:hsla(0,0%,100%,.95);backdrop-filter:blur(8px);display:flex;justify-content:center;align-items:center;z-index:9999}.loading-content{text-align:center}.loader{border:4px solid rgba(139,92,246,.1);border-top:4px solid #8b5cf6;border-radius:50%;width:60px;height:60px;animation:spin .8s linear infinite;margin:0 auto}.loading-text{margin-top:1.5rem}.loading-text h3{font-size:1.25rem;font-weight:600;color:#111827;margin-bottom:.5rem}.loading-text p{font-size:.875rem;color:#4b5563}.search-container{max-width:800px;margin:0 auto;padding:2rem 1rem}.search-form{width:100%}.search-input-group{display:flex;align-items:center;background:#fff;border-radius:9999px;padding:.5rem;box-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 10px 10px -5px rgba(0,0,0,.04);transition:all 300ms cubic-bezier(0.4, 0, 0.2, 1)}.search-input-group:focus-within{box-shadow:0 20px 40px rgba(139,92,246,.2);transform:translateY(-2px)}.search-icon{padding:0 1rem;color:#9ca3af;font-size:1.25rem}.search-input{flex:1;border:none;outline:none;padding:.875rem 1rem;font-size:1rem;background:rgba(0,0,0,0);color:#111827}.search-input::placeholder{color:#9ca3af}.search-button{background:#8b5cf6;color:#fff;border:none;border-radius:9999px;padding:.875rem 2rem;font-weight:600;font-size:1rem;cursor:pointer;display:flex;align-items:center;gap:.5rem;transition:all 300ms cubic-bezier(0.4, 0, 0.2, 1);white-space:nowrap}.search-button:hover{background:#7c3aed;transform:translateX(2px);box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05)}.search-button:active{transform:scale(0.95)}.search-button:focus{outline:2px solid #a78bfa;outline-offset:2px}.search-button.is-loading{position:relative;color:rgba(0,0,0,0);pointer-events:none}.search-button.is-loading::after{content:"";position:absolute;width:16px;height:16px;top:50%;left:50%;margin-left:-8px;margin-top:-8px;border:2px solid rgba(0,0,0,0);border-top-color:#fff;border-radius:50%;animation:spin .6s linear infinite}.search-help{margin-top:1rem;text-align:center;color:#4b5563;font-size:.875rem}.search-help i{margin-right:.25rem;color:#8b5cf6}button,a,input,.swot-card,.swot-card__icon{transition:all 300ms cubic-bezier(0.4, 0, 0.2, 1)}:focus-visible{outline:2px solid #8b5cf6;outline-offset:2px}.gradient-hero{background:linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;position:relative;overflow:hidden}.hero-icon img{filter:brightness(0) invert(1);animation:float 3s ease-in-out infinite}.hero-icon img:hover{animation:float 1s ease-in-out infinite;filter:brightness(0) invert(1) drop-shadow(0 0 20px rgba(255, 255, 255, 0.5))}.hero-cta .button{animation:pulse 2s infinite}.swot-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(300px, 1fr));gap:1.5rem;margin-top:2rem}.swot-card{background:#fff;border-radius:16px;padding:1.5rem;box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);transition:all 300ms cubic-bezier(0.4, 0, 0.2, 1);border-top:4px solid var(--card-color);animation:slideUp .5s ease-out backwards}.swot-card:hover{transform:translateY(-4px);box-shadow:0 25px 50px -12px rgba(0,0,0,.25)}.swot-card:hover .swot-card__icon{animation:iconPulse .5s ease-out}.swot-card--strength{--card-color: #10B981}.swot-card--weakness{--card-color: #F59E0B}.swot-card--opportunity{--card-color: #3B82F6}.swot-card--threat{--card-color: #EF4444}.swot-card__header{display:flex;align-items:center;gap:.75rem;margin-bottom:1rem;padding-bottom:1rem;border-bottom:1px solid #e5e7eb}.swot-card__icon{width:48px;height:48px;border-radius:12px;background:var(--card-color);display:flex;align-items:center;justify-content:center;color:#fff;font-size:1.5rem;flex-shrink:0}.swot-card__title{font-weight:600;font-size:1.375rem;color:#111827;flex-grow:1;margin:0}.swot-card__count{background:#f3f4f6;padding:.25rem .75rem;border-radius:20px;font-size:.875rem;font-weight:600;color:#374151;flex-shrink:0}.swot-card__body{overflow:hidden}.swot-list{list-style:none;padding:0;margin:0}.swot-list__item{display:flex;align-items:flex-start;gap:.75rem;padding:.75rem 0;border-bottom:1px solid #f3f4f6;animation:fadeIn .3s ease-out backwards}.swot-list__item:last-child{border-bottom:none}.swot-list__item:nth-child(1){animation-delay:0.1s}.swot-list__item:nth-child(2){animation-delay:0.2s}.swot-list__item:nth-child(3){animation-delay:0.3s}.swot-list__item:nth-child(4){animation-delay:0.4s}.swot-list__item:nth-child(5){animation-delay:0.5s}.swot-list__item:nth-child(6){animation-delay:0.6s}.swot-list__item:nth-child(7){animation-delay:0.7s}.swot-list__item:nth-child(8){animation-delay:0.8s}.swot-list__item:nth-child(9){animation-delay:0.9s}.swot-list__item:nth-child(10){animation-delay:1s}.swot-list__bullet{width:6px;height:6px;border-radius:50%;background:var(--card-color);margin-top:.5rem;flex-shrink:0}.swot-list__text{flex:1;color:#374151;line-height:1.6}.swot-card:nth-child(1){animation-delay:0.1s}.swot-card:nth-child(2){animation-delay:0.2s}.swot-card:nth-child(3){animation-delay:0.3s}.swot-card:nth-child(4){animation-delay:0.4s}#result-container.animate-in{animation:containerFadeIn .6s ease-out}.status-timeline{position:relative;padding-left:2rem}.status-timeline::before{content:"";position:absolute;left:14px;top:24px;bottom:24px;width:2px;background:#e5e7eb}.status-item{position:relative;padding:1rem 0 1rem 2rem;animation:statusFadeIn .3s ease-out}.status-item--info .status-item__indicator,.status-item--info .status-item__header{color:#3b82f6}.status-item--loading .status-item__indicator,.status-item--loading .status-item__header{color:#f59e0b}.status-item--loading .status-item__content{animation:statusPulse 2s ease-in-out infinite}.status-item--success .status-item__indicator,.status-item--success .status-item__header{color:#10b981}.status-item--error .status-item__indicator,.status-item--error .status-item__header{color:#ef4444}.status-item__indicator{position:absolute;left:0;top:1.25rem;width:30px;height:30px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1rem;background:#fff;box-shadow:0 0 0 4px #fff;z-index:1}.status-item__content{background:#fff;border-radius:12px;padding:1rem 1.25rem;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);transition:all 300ms cubic-bezier(0.4, 0, 0.2, 1)}.status-item:hover .status-item__content{box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);transform:translateX(4px)}.status-item__header{font-weight:600;font-size:.875rem;text-transform:uppercase;letter-spacing:.05em;margin-bottom:.5rem}.status-item__message{color:#374151;line-height:1.6;font-size:.9375rem}.empty-state{display:flex;align-items:center;justify-content:center;min-height:400px;padding:3rem 1.5rem}.empty-state__content{text-align:center;max-width:500px}.empty-state__icon{width:120px;height:120px;margin:0 auto 2rem;border-radius:50%;background:linear-gradient(135deg, #F3F4F6 0%, #E5E7EB 100%);display:flex;align-items:center;justify-content:center;font-size:3rem;color:#9ca3af;animation:float 3s ease-in-out infinite}.empty-state__title{font-size:1.75rem;font-weight:600;color:#1f2937;margin-bottom:.75rem}.empty-state__description{font-size:1.125rem;color:#4b5563;line-height:1.6;margin-bottom:1.5rem}.empty-state__cta{display:inline-flex;align-items:center;gap:.5rem;padding:.875rem 2rem;background:#8b5cf6;color:#fff;border-radius:9999px;text-decoration:none;font-weight:600;transition:all 300ms cubic-bezier(0.4, 0, 0.2, 1);box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06)}.empty-state__cta:hover{background:#7c3aed;transform:translateY(-2px);box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);color:#fff}.error-state{display:flex;align-items:center;justify-content:center;min-height:400px;padding:3rem 1.5rem}.error-state__content{text-align:center;max-width:500px}.error-state__icon{width:120px;height:120px;margin:0 auto 2rem;border-radius:50%;background:linear-gradient(135deg, #FEE2E2 0%, #FEF2F2 100%);display:flex;align-items:center;justify-content:center;font-size:3rem;color:#ef4444;animation:shake .5s ease-in-out}.error-state__title{font-size:1.75rem;font-weight:600;color:#1f2937;margin-bottom:.75rem}.error-state__description{font-size:1.125rem;color:#4b5563;line-height:1.6;margin-bottom:1.5rem}.error-state__details{background:#f3f4f6;border-radius:12px;padding:1rem;margin:1.5rem 0;font-family:"Courier New",monospace;font-size:.875rem;color:#374151;text-align:left;overflow-x:auto}.error-state__actions{display:flex;gap:1rem;justify-content:center;flex-wrap:wrap}.error-state__button{display:inline-flex;align-items:center;gap:.5rem;padding:.875rem 2rem;border-radius:9999px;font-weight:600;transition:all 300ms cubic-bezier(0.4, 0, 0.2, 1);box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);cursor:pointer;border:none;text-decoration:none}.error-state__button--primary{background:#8b5cf6;color:#fff}.error-state__button--primary:hover{background:#7c3aed;transform:translateY(-2px);box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);color:#fff}.error-state__button--secondary{background:#fff;color:#374151;border:2px solid #d1d5db}.error-state__button--secondary:hover{background:#f3f4f6;border-color:#9ca3af;transform:translateY(-2px);color:#1f2937}@media(max-width: 768px){.cell.is-col-span-2{grid-column:span 1}.swot-grid{grid-template-columns:1fr}.hero-title{font-size:2rem}.search-input-group{flex-direction:column;gap:.5rem;border-radius:20px;padding:1rem}.search-input{width:100%;text-align:center}.search-button{width:100%;justify-content:center}.swot-card__header{flex-wrap:wrap}.swot-card__count{order:-1;margin-left:auto}}@media(max-width: 480px){.swot-card{padding:1rem}.swot-card__icon{width:40px;height:40px;font-size:1.25rem}.swot-card__title{font-size:1.125rem}.hero-title{font-size:1.75rem}.status-timeline{padding-left:1.5rem}.status-item{padding-left:1.5rem}}:root{--brand-primary: #8B5CF6;--brand-primary-light: #A78BFA;--brand-primary-dark: #7C3AED;--swot-strength: #10B981;--swot-weakness: #F59E0B;--swot-opportunity: #3B82F6;--swot-threat: #EF4444;--neutral-50: #F9FAFB;--neutral-100: #F3F4F6;--neutral-200: #E5E7EB;--neutral-300: #D1D5DB;--neutral-400: #9CA3AF;--neutral-500: #6B7280;--neutral-600: #4B5563;--neutral-700: #374151;--neutral-800: #1F2937;--neutral-900: #111827;--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);--shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25);--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);--transition-base: 300ms cubic-bezier(0.4, 0, 0.2, 1);--transition-slow: 500ms cubic-bezier(0.4, 0, 0.2, 1)}/*# sourceMappingURL=pygentic_ai.css.map */ + */@import"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap";body{font-family:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;font-feature-settings:"cv02","cv03","cv04","cv11";-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.title,.subtitle,h1,h2,h3,h4,h5,h6{font-family:"Space Grotesk","Inter",sans-serif;letter-spacing:-0.02em;font-weight:600}.hero-title{font-size:clamp(2.5rem,5vw,4rem);font-weight:700;background:linear-gradient(135deg, #8B5CF6 0%, #6D28D9 100%);-webkit-background-clip:text;-webkit-text-fill-color:rgba(0,0,0,0);background-clip:text;margin-bottom:1rem;line-height:1.1}@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}@keyframes float{0%,100%{transform:translateY(0px)}50%{transform:translateY(-10px)}}@keyframes pulse{0%,100%{box-shadow:0 0 0 0 rgba(139,92,246,.7)}50%{box-shadow:0 0 0 10px rgba(139,92,246,0)}}@keyframes slideUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{from{opacity:0}to{opacity:1}}@keyframes shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-10px)}75%{transform:translateX(10px)}}@keyframes iconPulse{0%,100%{transform:scale(1)}50%{transform:scale(1.1)}}@keyframes containerFadeIn{from{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)}}@keyframes statusFadeIn{from{opacity:0;transform:translateX(-10px)}to{opacity:1;transform:translateX(0)}}@keyframes statusPulse{0%,100%{box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06)}50%{box-shadow:0 4px 20px rgba(245,158,11,.3)}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0}.skip-link{position:absolute;top:-40px;left:0;background:#8b5cf6;color:#fff;padding:.75rem 1.5rem;text-decoration:none;font-weight:600;z-index:10000;border-radius:0 0 12px 0;transition:top 150ms cubic-bezier(0.4, 0, 0.2, 1)}.skip-link:focus{top:0}.spinner-wrapper{position:fixed;top:0;left:0;width:100%;height:100%;background-color:hsla(0,0%,100%,.95);backdrop-filter:blur(8px);display:flex;justify-content:center;align-items:center;z-index:9999}.loading-content{text-align:center}.loader{border:4px solid rgba(139,92,246,.1);border-top:4px solid #8b5cf6;border-radius:50%;width:60px;height:60px;animation:spin .8s linear infinite;margin:0 auto}.loading-text{margin-top:1.5rem}.loading-text h3{font-size:1.25rem;font-weight:600;color:#111827;margin-bottom:.5rem}.loading-text p{font-size:.875rem;color:#4b5563}.search-container{max-width:800px;margin:0 auto;padding:2rem 1rem}.search-form{width:100%}.search-input-group{display:flex;align-items:center;background:#fff;border-radius:9999px;padding:.5rem;box-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 10px 10px -5px rgba(0,0,0,.04);transition:all 300ms cubic-bezier(0.4, 0, 0.2, 1)}.search-input-group:focus-within{box-shadow:0 20px 40px rgba(139,92,246,.2);transform:translateY(-2px)}.search-icon{padding:0 1rem;color:#9ca3af;font-size:1.25rem}.search-input{flex:1;border:none;outline:none;padding:.875rem 1rem;font-size:1rem;background:rgba(0,0,0,0);color:#111827}.search-input::placeholder{color:#9ca3af}.search-button{background:#8b5cf6;color:#fff;border:none;border-radius:9999px;padding:.875rem 2rem;font-weight:600;font-size:1rem;cursor:pointer;display:flex;align-items:center;gap:.5rem;transition:all 300ms cubic-bezier(0.4, 0, 0.2, 1);white-space:nowrap}.search-button:hover{background:#7c3aed;transform:translateX(2px);box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05)}.search-button:active{transform:scale(0.95)}.search-button:focus{outline:2px solid #a78bfa;outline-offset:2px}.search-button.is-loading{position:relative;color:rgba(0,0,0,0);pointer-events:none}.search-button.is-loading::after{content:"";position:absolute;width:16px;height:16px;top:50%;left:50%;margin-left:-8px;margin-top:-8px;border:2px solid rgba(0,0,0,0);border-top-color:#fff;border-radius:50%;animation:spin .6s linear infinite}.search-help{margin-top:1rem;text-align:center;color:#4b5563;font-size:.875rem}.search-help i{margin-right:.25rem;color:#8b5cf6}button,a,input,.swot-card,.swot-card__icon{transition:all 300ms cubic-bezier(0.4, 0, 0.2, 1)}:focus-visible{outline:2px solid #8b5cf6;outline-offset:2px}.gradient-hero{background:linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;position:relative;overflow:hidden}.hero-icon img{filter:brightness(0) invert(1);animation:float 3s ease-in-out infinite}.hero-icon img:hover{animation:float 1s ease-in-out infinite;filter:brightness(0) invert(1) drop-shadow(0 0 20px rgba(255, 255, 255, 0.5))}.hero-cta .button{animation:pulse 2s infinite}.swot-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(300px, 1fr));gap:1.5rem;margin-top:2rem}.swot-card{background:#fff;border-radius:16px;padding:1.5rem;box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);transition:all 300ms cubic-bezier(0.4, 0, 0.2, 1);border-top:4px solid var(--card-color);animation:slideUp .5s ease-out backwards}.swot-card:hover{transform:translateY(-4px);box-shadow:0 25px 50px -12px rgba(0,0,0,.25)}.swot-card:hover .swot-card__icon{animation:iconPulse .5s ease-out}.swot-card--strength{--card-color: #10B981}.swot-card--weakness{--card-color: #F59E0B}.swot-card--opportunity{--card-color: #3B82F6}.swot-card--threat{--card-color: #EF4444}.swot-card__header{display:flex;align-items:center;gap:.75rem;margin-bottom:1rem;padding-bottom:1rem;border-bottom:1px solid #e5e7eb}.swot-card__icon{width:48px;height:48px;border-radius:12px;background:var(--card-color);display:flex;align-items:center;justify-content:center;color:#fff;font-size:1.5rem;flex-shrink:0}.swot-card__title{font-weight:600;font-size:1.375rem;color:#111827;flex-grow:1;margin:0}.swot-card__count{background:#f3f4f6;padding:.25rem .75rem;border-radius:20px;font-size:.875rem;font-weight:600;color:#374151;flex-shrink:0}.swot-card__body{overflow:hidden}.swot-list{list-style:none;padding:0;margin:0}.swot-list__item{display:flex;align-items:flex-start;gap:.75rem;padding:.75rem 0;border-bottom:1px solid #f3f4f6;animation:fadeIn .3s ease-out backwards}.swot-list__item:last-child{border-bottom:none}.swot-list__item:nth-child(1){animation-delay:0.1s}.swot-list__item:nth-child(2){animation-delay:0.2s}.swot-list__item:nth-child(3){animation-delay:0.3s}.swot-list__item:nth-child(4){animation-delay:0.4s}.swot-list__item:nth-child(5){animation-delay:0.5s}.swot-list__item:nth-child(6){animation-delay:0.6s}.swot-list__item:nth-child(7){animation-delay:0.7s}.swot-list__item:nth-child(8){animation-delay:0.8s}.swot-list__item:nth-child(9){animation-delay:0.9s}.swot-list__item:nth-child(10){animation-delay:1s}.swot-list__bullet{width:6px;height:6px;border-radius:50%;background:var(--card-color);margin-top:.5rem;flex-shrink:0}.swot-list__text{flex:1;color:#374151;line-height:1.6}.swot-card:nth-child(1){animation-delay:0.1s}.swot-card:nth-child(2){animation-delay:0.2s}.swot-card:nth-child(3){animation-delay:0.3s}.swot-card:nth-child(4){animation-delay:0.4s}#result-container.animate-in{animation:containerFadeIn .6s ease-out}.status-timeline{position:relative;padding-left:2rem}.status-timeline::before{content:"";position:absolute;left:14px;top:24px;bottom:24px;width:2px;background:#e5e7eb}.status-item{position:relative;padding:1rem 0 1rem 2rem;animation:statusFadeIn .3s ease-out}.status-item--info .status-item__indicator,.status-item--info .status-item__header{color:#3b82f6}.status-item--loading .status-item__indicator,.status-item--loading .status-item__header{color:#f59e0b}.status-item--loading .status-item__content{animation:statusPulse 2s ease-in-out infinite}.status-item--success .status-item__indicator,.status-item--success .status-item__header{color:#10b981}.status-item--error .status-item__indicator,.status-item--error .status-item__header{color:#ef4444}.status-item__indicator{position:absolute;left:0;top:1.25rem;width:30px;height:30px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1rem;background:#fff;box-shadow:0 0 0 4px #fff;z-index:1}.status-item__content{background:#fff;border-radius:12px;padding:1rem 1.25rem;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);transition:all 300ms cubic-bezier(0.4, 0, 0.2, 1)}.status-item:hover .status-item__content{box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);transform:translateX(4px)}.status-item__header{font-weight:600;font-size:.875rem;text-transform:uppercase;letter-spacing:.05em;margin-bottom:.5rem}.status-item__message{color:#374151;line-height:1.6;font-size:.9375rem}.empty-state{display:flex;align-items:center;justify-content:center;min-height:400px;padding:3rem 1.5rem}.empty-state__content{text-align:center;max-width:500px}.empty-state__icon{width:120px;height:120px;margin:0 auto 2rem;border-radius:50%;background:linear-gradient(135deg, #F3F4F6 0%, #E5E7EB 100%);display:flex;align-items:center;justify-content:center;font-size:3rem;color:#9ca3af;animation:float 3s ease-in-out infinite}.empty-state__title{font-size:1.75rem;font-weight:600;color:#1f2937;margin-bottom:.75rem}.empty-state__description{font-size:1.125rem;color:#4b5563;line-height:1.6;margin-bottom:1.5rem}.empty-state__cta{display:inline-flex;align-items:center;gap:.5rem;padding:.875rem 2rem;background:#8b5cf6;color:#fff;border-radius:9999px;text-decoration:none;font-weight:600;transition:all 300ms cubic-bezier(0.4, 0, 0.2, 1);box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06)}.empty-state__cta:hover{background:#7c3aed;transform:translateY(-2px);box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);color:#fff}.error-state{display:flex;align-items:center;justify-content:center;min-height:400px;padding:3rem 1.5rem}.error-state__content{text-align:center;max-width:500px}.error-state__icon{width:120px;height:120px;margin:0 auto 2rem;border-radius:50%;background:linear-gradient(135deg, #FEE2E2 0%, #FEF2F2 100%);display:flex;align-items:center;justify-content:center;font-size:3rem;color:#ef4444;animation:shake .5s ease-in-out}.error-state__title{font-size:1.75rem;font-weight:600;color:#1f2937;margin-bottom:.75rem}.error-state__description{font-size:1.125rem;color:#4b5563;line-height:1.6;margin-bottom:1.5rem}.error-state__details{background:#f3f4f6;border-radius:12px;padding:1rem;margin:1.5rem 0;font-family:"Courier New",monospace;font-size:.875rem;color:#374151;text-align:left;overflow-x:auto}.error-state__actions{display:flex;gap:1rem;justify-content:center;flex-wrap:wrap}.error-state__button{display:inline-flex;align-items:center;gap:.5rem;padding:.875rem 2rem;border-radius:9999px;font-weight:600;transition:all 300ms cubic-bezier(0.4, 0, 0.2, 1);box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);cursor:pointer;border:none;text-decoration:none}.error-state__button--primary{background:#8b5cf6;color:#fff}.error-state__button--primary:hover{background:#7c3aed;transform:translateY(-2px);box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);color:#fff}.error-state__button--secondary{background:#fff;color:#374151;border:2px solid #d1d5db}.error-state__button--secondary:hover{background:#f3f4f6;border-color:#9ca3af;transform:translateY(-2px);color:#1f2937}@media(max-width: 768px){.cell.is-col-span-2{grid-column:span 1}.swot-grid{grid-template-columns:1fr}.hero-title{font-size:2rem}.search-input-group{flex-direction:column;gap:.5rem;border-radius:20px;padding:1rem}.search-input{width:100%;text-align:center}.search-button{width:100%;justify-content:center}.swot-card__header{flex-wrap:wrap}.swot-card__count{order:-1;margin-left:auto}}@media(max-width: 480px){.swot-card{padding:1rem}.swot-card__icon{width:40px;height:40px;font-size:1.25rem}.swot-card__title{font-size:1.125rem}.hero-title{font-size:1.75rem}.status-timeline{padding-left:1.5rem}.status-item{padding-left:1.5rem}}:root{--brand-primary: #8B5CF6;--brand-primary-light: #A78BFA;--brand-primary-dark: #7C3AED;--swot-strength: #10B981;--swot-weakness: #F59E0B;--swot-opportunity: #3B82F6;--swot-threat: #EF4444;--neutral-50: #F9FAFB;--neutral-100: #F3F4F6;--neutral-200: #E5E7EB;--neutral-300: #D1D5DB;--neutral-400: #9CA3AF;--neutral-500: #6B7280;--neutral-600: #4B5563;--neutral-700: #374151;--neutral-800: #1F2937;--neutral-900: #111827;--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);--shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25);--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);--transition-base: 300ms cubic-bezier(0.4, 0, 0.2, 1);--transition-slow: 500ms cubic-bezier(0.4, 0, 0.2, 1)}/*# sourceMappingURL=pygentic_ai.css.map */ diff --git a/src/frontend/static/css/pygentic_ai.css.map b/src/frontend/static/css/pygentic_ai.css.map index 0aa385c..5a4fa07 100644 --- a/src/frontend/static/css/pygentic_ai.css.map +++ b/src/frontend/static/css/pygentic_ai.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/styles.scss","../../scss/_variables.scss","../../scss/_typography.scss","../../scss/_animations.scss","../../scss/_components.scss","../../scss/_layout.scss","../../scss/_states.scss","../../scss/_responsive.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA,GCMQ,gICFR,KACE,YDIU,8FCHV,kDACA,mCACA,kCAGF,mCAGE,YDJa,mCCKb,uBACA,gBAIF,YACE,iCACA,gBACA,WDmBkB,kDClBlB,6BACA,sCACA,qBACA,mBACA,gBCxBF,gBACE,KACE,uBAEF,GACE,0BAIJ,iBACE,QACE,0BAEF,IACE,6BAIJ,iBACE,QACE,uCAEF,IACE,0CAIJ,mBACE,KACE,UACA,2BAEF,GACE,UACA,yBAIJ,kBACE,KACE,UAEF,GACE,WAIJ,iBACE,gCACA,gCACA,gCAGF,qBACE,QACE,mBAEF,IACE,sBAIJ,2BACE,KACE,UACA,2BAEF,GACE,UACA,yBAIJ,wBACE,KACE,UACA,4BAEF,GACE,UACA,yBAIJ,uBACE,QACE,WF3CQ,6DE6CV,IACE,2CCtFJ,iBACE,eACA,MACA,OACA,WACA,YACA,qCACA,0BACA,aACA,uBACA,mBACA,aAGF,iBACE,kBAGF,QACE,qCACA,6BACA,kBACA,WACA,YACA,mCACA,cAGF,cACE,kBAEA,iBACE,kBACA,gBACA,MHHU,QGIV,oBAGF,gBACE,kBACA,MHZU,QGkBd,kBACE,gBACA,cACA,kBAGF,aACE,WAGF,oBACE,aACA,mBACA,gBACA,cHSY,OGRZ,cACA,WHpBU,iEGqBV,kDAEA,iCACE,2CACA,2BAIJ,aACE,eACA,MH/CY,QGgDZ,kBAGF,cACE,OACA,YACA,aACA,qBACA,eACA,yBACA,MHrDY,QGuDZ,2BACE,MH7DU,QGiEd,eACE,WHtFc,QGuFd,WACA,YACA,cH1BY,OG2BZ,qBACA,gBACA,eACA,eACA,aACA,mBACA,UACA,kDACA,mBAEA,qBACE,WHnGiB,QGoGjB,0BACA,WHpEQ,+DGuEV,sBACE,sBAGF,qBACE,0BACA,mBAGF,0BACE,kBACA,oBACA,oBAEA,iCACE,WACA,kBACA,WACA,YACA,QACA,SACA,iBACA,gBACA,+BACA,sBACA,kBACA,mCAKN,aACE,gBACA,kBACA,MHtHY,QGuHZ,kBAEA,eACE,oBACA,MHjJY,QGsJhB,2CAKE,kDAIF,eACE,0BA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","file":"pygentic_ai.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/styles.scss","../../scss/_variables.scss","../../scss/_typography.scss","../../scss/_animations.scss","../../scss/_components.scss","../../scss/_layout.scss","../../scss/_states.scss","../../scss/_responsive.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA,GCMQ,gICFR,KACE,YDIU,8FCHV,kDACA,mCACA,kCAGF,mCAGE,YDJa,mCCKb,uBACA,gBAIF,YACE,iCACA,gBACA,WDmBkB,kDClBlB,6BACA,sCACA,qBACA,mBACA,gBCxBF,gBACE,KACE,uBAEF,GACE,0BAIJ,iBACE,QACE,0BAEF,IACE,6BAIJ,iBACE,QACE,uCAEF,IACE,0CAIJ,mBACE,KACE,UACA,2BAEF,GACE,UACA,yBAIJ,kBACE,KACE,UAEF,GACE,WAIJ,iBACE,gCACA,gCACA,gCAGF,qBACE,QACE,mBAEF,IACE,sBAIJ,2BACE,KACE,UACA,2BAEF,GACE,UACA,yBAIJ,wBACE,KACE,UACA,4BAEF,GACE,UACA,yBAIJ,uBACE,QACE,WF3CQ,6DE6CV,IACE,2CCtFJ,SACE,kBACA,UACA,WACA,UACA,YACA,gBACA,sBACA,mBACA,eAGF,WACE,kBACA,UACA,OACA,WHVc,QGWd,WACA,sBACA,qBACA,gBACA,cACA,yBACA,kDAEA,iBACE,MAMJ,iBACE,eACA,MACA,OACA,WACA,YACA,qCACA,0BACA,aACA,uBACA,mBACA,aAGF,iBACE,kBAGF,QACE,qCACA,6BACA,kBACA,WACA,YACA,mCACA,cAGF,cACE,kBAEA,iBACE,kBACA,gBACA,MHnCU,QGoCV,oBAGF,gBACE,kBACA,MH5CU,QGkDd,kBACE,gBACA,cACA,kBAGF,aACE,WAGF,oBACE,aACA,mBACA,gBACA,cHvBY,OGwBZ,cACA,WHpDU,iEGqDV,kDAEA,iCACE,2CACA,2BAIJ,aACE,eACA,MH/EY,QGgFZ,kBAGF,cACE,OACA,YACA,aACA,qBACA,eACA,yBACA,MHrFY,QGuFZ,2BACE,MH7FU,QGiGd,eACE,WHtHc,QGuHd,WACA,YACA,cH1DY,OG2DZ,qBACA,gBACA,eACA,eACA,aACA,mBACA,UACA,kDACA,mBAEA,qBACE,WHnIiB,QGoIjB,0BACA,WHpGQ,+DGuGV,sBACE,sBAGF,qBACE,0BACA,mBAGF,0BACE,kBACA,oBACA,oBAEA,iCACE,WACA,kBACA,WACA,YACA,QACA,SACA,iBACA,gBACA,+BACA,sBACA,kBACA,mCAKN,aACE,gBACA,kBACA,MHtJY,QGuJZ,kBAEA,eACE,oBACA,MHjLY,QGsLhB,2CAKE,kDAIF,eACE,0BACA,mBCvMF,eACE,wEACA,kBACA,gBAIA,eACE,+BACA,wCAEA,qBACE,wCACA,8EAKN,kBACE,4BAKF,WACE,aACA,2DACA,IJiCW,OIhCX,WJiCW,KI9Bb,WACE,gBACA,cJkCU,KIjCV,QJ0BW,OIzBX,WJMU,+DILV,kDACA,uCACA,yCAEA,iBACE,2BACA,WJCS,kCICT,kCACE,iCAKJ,qBACE,sBAGF,qBACE,sBAGF,wBACE,sBAGF,mBACE,sBAIJ,mBACE,aACA,mBACA,WACA,mBACA,oBACA,gCAGF,iBACE,WACA,YACA,cJbU,KIcV,6BACA,aACA,mBACA,uBACA,WACA,iBACA,cAGF,kBACE,gBACA,mBACA,MJ7DY,QI8DZ,YACA,SAGF,kBACE,WJ3EY,QI4EZ,sBACA,cJhCU,KIiCV,kBACA,gBACA,MJ1EY,QI2EZ,cAGF,iBACE,gBAGF,WACE,gBACA,UACA,SAGF,iBACE,aACA,uBACA,WACA,iBACA,gCACA,wCAEA,4BACE,mBAKA,8BACE,qBADF,8BACE,qBADF,8BACE,qBADF,8BACE,qBADF,8BACE,qBADF,8BACE,qBADF,8BACE,qBADF,8BACE,qBADF,8BACE,qBADF,+BACE,mBAKN,mBACE,UACA,WACA,kBACA,6BACA,iBACA,cAGF,iBACE,OACA,MJvHY,QIwHZ,gBAKA,wBACE,qBADF,wBACE,qBADF,wBACE,qBADF,wBACE,qBAKJ,6BACE,uCAKF,iBACE,kBACA,kBAEA,yBACE,WACA,kBACA,UACA,SACA,YACA,UACA,WJzJU,QI6Jd,aACE,kBACA,yBACA,oCAIE,mFAEE,MJ9Ka,QImLf,yFAEE,MJvLU,QI0LZ,4CACE,8CAKF,yFAEE,MJpMU,QIyMZ,qFAEE,MJrMQ,QI0Md,wBACE,kBACA,OACA,YACA,WACA,YACA,kBACA,aACA,mBACA,uBACA,eACA,gBACA,0BACA,UAGF,sBACE,gBACA,cJ5KU,KI6KV,qBACA,WJxMU,6DIyMV,kDAGF,yCACE,WJ5MU,+DI6MV,0BAGF,qBACE,gBACA,kBACA,yBACA,qBACA,oBAGF,sBACE,MJrOY,QIsOZ,gBACA,mBCpQF,aACE,aACA,mBACA,uBACA,iBACA,oBAGF,sBACE,kBACA,gBAGF,mBACE,YACA,aACA,mBACA,kBACA,6DACA,aACA,mBACA,uBACA,eACA,MLGY,QKFZ,wCAGF,oBACE,kBACA,gBACA,cACA,qBAGF,0BACE,mBACA,MLRY,QKSZ,gBACA,qBAGF,kBACE,oBACA,mBACA,UACA,qBACA,WLxCc,QKyCd,WACA,cLqBY,OKpBZ,qBACA,gBACA,kDACA,WLZU,6DKcV,wBACE,WL/CiB,QKgDjB,2BACA,WLhBQ,+DKiBR,WAMJ,aACE,aACA,mBACA,uBACA,iBACA,oBAGF,sBACE,kBACA,gBAGF,mBACE,YACA,aACA,mBACA,kBACA,6DACA,aACA,mBACA,uBACA,eACA,MLrEY,QKsEZ,gCAGF,oBACE,kBACA,gBACA,MLhEY,QKiEZ,qBAGF,0BACE,mBACA,MLxEY,QKyEZ,gBACA,qBAGF,sBACE,WLnFY,QKoFZ,cLzCU,KK0CV,aACA,gBACA,oCACA,kBACA,MLnFY,QKoFZ,gBACA,gBAGF,sBACE,aACA,SACA,uBACA,eAGF,qBACE,oBACA,mBACA,UACA,qBACA,cL5DY,OK6DZ,gBACA,kDACA,WL5FU,6DK6FV,eACA,YACA,qBAEA,8BACE,WLpIY,QKqIZ,WAEA,oCACE,WLtIe,QKuIf,2BACA,WLvGM,+DKwGN,WAIJ,gCACE,gBACA,ML1HU,QK2HV,yBAEA,sCACE,WLpIQ,QKqIR,aLlIQ,QKmIR,2BACA,MLhIQ,QM/Bd,yBACE,oBACE,mBAGF,WACE,0BAGF,YACE,eAGF,oBACE,sBACA,UACA,cNqDQ,KMpDR,aAGF,cACE,WACA,kBAGF,eACE,WACA,uBAGF,mBACE,eAGF,kBACE,SACA,kBAKJ,yBACE,WACE,aAGF,iBACE,WACA,YACA,kBAGF,kBACE,mBAGF,YACE,kBAGF,iBACE,oBAGF,aACE,qBP3CJ,MAEE,yBACA,+BACA,8BAGA,yBACA,yBACA,4BACA,uBAGA,sBACA,uBACA,uBACA,uBACA,uBACA,uBACA,uBACA,uBACA,uBACA,uBAGA,6CACA,mFACA,qFACA,uFACA,oDAGA,sDACA,sDACA","file":"pygentic_ai.css"} \ No newline at end of file diff --git a/src/frontend/static/js/app.js b/src/frontend/static/js/app.js index 85fcffe..9833c2f 100644 --- a/src/frontend/static/js/app.js +++ b/src/frontend/static/js/app.js @@ -71,15 +71,47 @@ } /** - * Smooth scroll to results + * Announce to screen readers + */ + function announceToScreenReader(message, priority = 'polite') { + const announcement = document.createElement('div'); + announcement.setAttribute('role', 'status'); + announcement.setAttribute('aria-live', priority); + announcement.setAttribute('aria-atomic', 'true'); + announcement.className = 'sr-only'; + announcement.textContent = message; + + document.body.appendChild(announcement); + + // Remove after announcement is made + setTimeout(() => { + document.body.removeChild(announcement); + }, 1000); + } + + /** + * Smooth scroll to results and manage focus */ function scrollToResults() { const resultsSection = document.getElementById('result-container'); + const resultsHeading = document.getElementById('results-heading'); + if (resultsSection) { + // Announce completion to screen readers + announceToScreenReader('Analysis complete. Results are now available.', 'assertive'); + + // Scroll to results resultsSection.scrollIntoView({ behavior: 'smooth', block: 'start' }); + + // Move focus to results heading for keyboard users + if (resultsHeading) { + setTimeout(() => { + resultsHeading.focus(); + }, 600); + } } } @@ -96,6 +128,44 @@ return pollInterval; } + /** + * Initialize keyboard navigation for SWOT cards + */ + function initializeKeyboardNavigation() { + const cards = document.querySelectorAll('.swot-card'); + + cards.forEach((card, index) => { + card.addEventListener('keydown', function(e) { + let targetCard = null; + + switch (e.key) { + case 'ArrowRight': + case 'ArrowDown': + e.preventDefault(); + targetCard = cards[index + 1] || cards[0]; + break; + case 'ArrowLeft': + case 'ArrowUp': + e.preventDefault(); + targetCard = cards[index - 1] || cards[cards.length - 1]; + break; + case 'Home': + e.preventDefault(); + targetCard = cards[0]; + break; + case 'End': + e.preventDefault(); + targetCard = cards[cards.length - 1]; + break; + } + + if (targetCard) { + targetCard.focus(); + } + }); + }); + } + /** * Initialize form submission handler */ @@ -104,6 +174,9 @@ if (!form) return; form.addEventListener('submit', function(e) { + // Announce to screen readers + announceToScreenReader('Analysis started. Please wait while we process your request.', 'assertive'); + // Start loading messages when form is submitted startLoadingMessages(); @@ -135,6 +208,9 @@ spinner.classList.add('is-hidden'); } + // Initialize keyboard navigation for SWOT cards + initializeKeyboardNavigation(); + // Scroll to results after a brief delay setTimeout(scrollToResults, 500); @@ -219,6 +295,7 @@ window.PygenticAI = { startLoadingMessages, stopLoadingMessages, - scrollToResults + scrollToResults, + announceToScreenReader }; })(); diff --git a/src/frontend/templates/components/main/base.html b/src/frontend/templates/components/main/base.html index 2481de2..1122b4c 100644 --- a/src/frontend/templates/components/main/base.html +++ b/src/frontend/templates/components/main/base.html @@ -8,9 +8,13 @@ + {% include "components/main/nav.html" %} - {% block content%} - {% endblock %} +
+ {% block content%} + {% endblock %} +
{% block js_content %} {% endblock js_content %} {% include "components/snippets/js.html" %} diff --git a/src/frontend/templates/components/snippets/EmptyState.jinja b/src/frontend/templates/components/snippets/EmptyState.jinja index 01d3f01..ce038eb 100644 --- a/src/frontend/templates/components/snippets/EmptyState.jinja +++ b/src/frontend/templates/components/snippets/EmptyState.jinja @@ -4,9 +4,12 @@ icon: str = "fa-chart-simple" #} -
+
-
+

{{ title }}

diff --git a/src/frontend/templates/components/snippets/ErrorState.jinja b/src/frontend/templates/components/snippets/ErrorState.jinja index baec9a4..056a288 100644 --- a/src/frontend/templates/components/snippets/ErrorState.jinja +++ b/src/frontend/templates/components/snippets/ErrorState.jinja @@ -5,16 +5,21 @@ show_retry: bool = True #} -
+