Add type="button" to all buttons created via innerHTML in JS:
- Lightbox close/prev/next buttons (3 instances)
- Pagination prev/page/ellipsis/next buttons (7 instances)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add ArrowUp/ArrowDown/Home/End keyboard navigation between context
menu items per WAI-ARIA menu widget specification. Focus wraps at
top and bottom boundaries.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two init paths both called runBoot() before sessionStorage was set
(the key was only written on completion, ~1.1s later):
- Private init() at DOMContentLoaded → runBoot()
- lt.init() HTML call on DOMContentLoaded → ltInit() → runBoot()
Both found no session key and started simultaneous setInterval loops
on the same <pre>, each appending the same message → every line twice.
Fix: set the sessionStorage key immediately at the start of runBoot()
so any concurrent second call exits early.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
HTML:
- Add type="button" to all buttons outside forms (22 instances)
- Add aria-label="Add comment" to unlabelled textarea#td-comment
JS:
- Escape alt text and link text in markdown renderer with escHtml()
to prevent XSS in image alt/link content
- Fix nested modal focus: only restore trigger focus when no other
modal is still open; add document.contains guard
CSS:
- Add .lt-nav-link:focus-visible focus ring (was missing entirely)
- Fix .lt-typeahead-option (dead selector) → .lt-typeahead-item with
:hover, .is-focused, and :focus-visible for light theme
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
JS:
- Lightbox: remove keydown listener on close (memory leak fix)
- Lightbox: restore focus to trigger on close
- Right drawer: fix aria-hidden="false" anti-pattern to removeAttribute
- Markdown renderer: block javascript:/data: protocol URIs in link and
image replacements to prevent XSS
- Sidebar submenus: add aria-expanded tracking on toggle; hide decorative
chevron from screen readers; initialize aria-expanded on mount
CSS:
- Add :focus-visible to .lt-sidebar-toggle (interactive button)
- Add :focus-visible to .lt-dropzone (focusable container)
- Fix .lt-stat-card:focus-visible outline-offset to -2px (clip-path clips it)
- Add light theme override for .lt-nav-drawer-link:focus-visible
- Adjust .lt-split-divider:focus-visible outline-offset to 3px
HTML:
- Range input: update aria-valuenow dynamically on input event
- Combobox label: add for="demo-combobox-input" association
- Typeahead label: add for="demo-typeahead-input" association
- Dropzone file input: add aria-label
- Notification items: add descriptive aria-label to all 4 items;
add aria-hidden="true" to decorative dot spans
- Mark all read button: add type="button" to prevent accidental form submit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- JS: fix checkbox/radio required validation using .checked not .value
- JS: guard _cpTrigger.focus() with document.contains() check
- JS: add arrow/Home/End key navigation to tab groups (WCAG 2.1)
- JS: clamp context menu left edge with Math.max(8, ...) to prevent off-screen
- JS: fix wizard _show() to removeAttribute aria-hidden on active step
- HTML: add role="region" + aria-label to notification panel
- HTML: convert Assigned To span+div to label+select with for/id association
- HTML: add role="article" tabindex="0" aria-label to all kanban cards
- HTML: remove aria-hidden="false" anti-pattern from wizard active step
- CSS/HTML/JS: replace aria-hidden="false" show-hook with :not([aria-hidden])
so open state is represented by absent attribute rather than false value
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Touch targets:
- All .lt-btn-sm, .lt-menu-btn, .lt-page-btn, .lt-dropdown-trigger,
.lt-notif-bell-btn now 44px minimum on pointer:coarse devices
Font size floors:
- Nothing below 0.72rem on XS; nav drawer, cmd palette, stats, timeline,
dropdown items, notification timestamps all bumped to readable sizes
Notification panel:
- width: min(300px,92vw) prevents overflow on 320px screens
- max-height: min(280px,calc(80vh-110px)) for landscape safety
- title: 2-line clamp instead of single-line truncate
- Header/time font sizes increased to 0.75rem/0.7rem
Dropdown panels:
- Advanced filter: clamp(200px,60vw,260px) instead of fixed 240px
- All dropdowns: max-width:90vw + proper alignment on SM/XS
- Dropdown items: 0.78-0.8rem, 36px min-height
Toolbar:
- toolbar-left/right now flex-wrap on SM+XS; search expands full-width
- Result count hidden on XS (.lt-hide-xs) to uncramp narrow toolbars
Drawer:
- Right drawer goes full-width (100vw) on SM with border-top
- Status/Priority 2-col form grid collapses to 1-col (.lt-drawer-form-grid)
Table card mode:
- Breakpoint corrected from rogue 640px → 767px (system breakpoint)
- Row min-height 36px, font bumped to 0.78rem
Kanban:
- Keeps 2-col at SM and XS (was collapsing to 1-col, confusing semantics)
Grids:
- Chart demo, sortable demo: auto-fit minmax so they reflow naturally
- Countdown stat grid: auto-fit minmax(160px,1fr) instead of fixed 3-col
- Grid gap reduced to var(--space-sm) at XS
Split pane:
- Demo height: clamp(160px,30vh,240px) instead of fixed 200px
- Stacks vertically on SM with horizontal divider
Modals:
- max-width: 640px cap prevents 4K-wide modals; overridden on SM/XS
Header:
- Brand title truncated at max-width:110px on XS
- WS status indicator hidden on XS to free header space
- Admin badge hidden on XS
- Safe-area insets applied to header-right and toast container
Landscape phone:
- Modal max-height respects viewport; notif list shrinks
Breakpoint consistency:
- Removed rogue 640px breakpoint; all queries use 479/767/1023/1279px system
Section 78 added with all targeted overrides.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix duplicate function showToast declaration causing infinite recursion
(showToast declared twice → function hoisting → _origShowToast === self)
Progress bar now inlined directly into _displayToast; Module 47 removed
- Notification bell now opens a proper dropdown panel with unread items,
per-item click-to-read, "Mark all read", close on outside click/Esc
- Ticket detail drawer now has real editable fields (title, status,
priority, assignee, description textarea, comment box) instead of
read-only KV pairs; Save Changes and Post Comment buttons functional
- Advanced ▾ filter dropdown: status/priority/assignee selects + Apply/Reset
- Bulk Actions dropdown: Close/Reassign/Export/Delete with toast feedback
- Generic .lt-dropdown-trigger toggle system (works for any future dropdown)
- Add CSS sections 76 (notification panel) and 77 (dropdown widget + .lt-textarea)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix lt.clipboard.init() → initCopyButtons() (crashed init handler
before context menu, split pane, lightbox, theme btn could register)
- Add lt.pagination module (Module 55) with ellipsis rendering,
prev/next, onChange callback; wire demo-pagination nav
- Upgrade lt.sortable for cross-list group dragging (shared module-level
drag state enables kanban card movement between columns)
- Wire kanban columns (open/pending/inprogress/closed) to lt.sortable
with group:'kanban' for drag-and-drop between columns
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause: two SyntaxErrors prevented the IIFE from completing,
meaning window.lt was never created and all features were broken.
Fixes applied:
1. CRITICAL: Renamed auth's apiFetch redeclaration → _apiFetchAuth
(duplicate function declaration in strict-mode IIFE = SyntaxError)
2. CRITICAL: Renamed toast queue vars (_toastQueue/_toastActive already
declared by original showToast in outer scope = SyntaxError)
Replaced heavy queue wrapper with lightweight progress-bar injector
3. timer.countdown: classList.add(urgentClass) throws on space-separated
class string — split/filter/forEach now used
4. contextMenu: _ctxItems declared as [] but used as string-keyed object
— changed to {}
5. Split pane divider: background was border-dim (7% opacity, invisible)
— raised to border-color (16%), added ::before hit-area expansion,
hover state uses accent-cyan for clear visual feedback
6. Light theme: html background-image was hardcoded cyan rgba, didn't
update with data-theme — added explicit html[data-theme="light"] rule
7. Light theme: .lt-header background was hardcoded rgba(3,5,8,0.96)
— added explicit light-mode override
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- base.js: Fix boot sequence title centering — old formula was off by 1
char for odd-length app names (PULSE, GANDALF). Remove unused `bar`
variable. New logic computes left/right padding independently to
handle both even and odd title lengths correctly.
- node/middleware.js: Prune expired rate-limit entries from Map when
size exceeds 5000 to prevent unbounded memory growth. Also use
req.socket?.remoteAddress as fallback for req.ip.
- README.md: Document lt.beep() in the JS API section. Clarify Quick
Start to distinguish core files from platform-specific helpers.
Note that tableNav.init() and sortTable.init() require explicit calls.
- aesthetic_diff.md: Correct §8 toast icon format — base.js uses
bracketed symbols [✓][✗][!][i], not >> prefix as previously stated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Unified CSS, JavaScript utilities, HTML template, and framework skeleton
files for Tinker Tickets (PHP), PULSE (Node.js), and GANDALF (Flask).
Includes aesthetic_diff.md documenting every divergence between the three
apps with prioritised recommendations for convergence.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>