11import type { Impact , PinTarget , Violation , ViolationNode } from '../shared/protocol.ts'
2- import type { SelectedItem } from './components /fix-prompts.tsx '
3- import type { RouteGroupModel , SelectionApi } from './components/violations.tsx '
2+ import type { SelectedItem } from './lib /fix-prompt.ts '
3+ import type { RouteGroupModel , SelectionApi } from './lib/violation-view.ts '
44import { batch , createEffect , createMemo , createSignal , Match , on , Show , Switch } from 'solid-js'
55import { emptyCounts } from '../shared/protocol.ts'
6- import { FixPromptsDialog } from './components/fix-prompts.tsx'
7- import { Header , MetaLine } from './components/header.tsx'
8- import { CheckCircle , PlugIcon } from './components/icons.tsx'
9- import { SummaryBar } from './components/summary.tsx'
10- import { ruleCardId , ViolationList } from './components/violations.tsx'
6+ import { EmptyState } from './components/EmptyState.tsx'
7+ import { FixPromptsDialog } from './components/FixPromptsDialog.tsx'
8+ import { Header } from './components/Header.tsx'
9+ import { MetaLine } from './components/MetaLine.tsx'
10+ import { SummaryBar } from './components/SummaryBar.tsx'
11+ import { ViolationList } from './components/ViolationList.tsx'
1112import { createA11yChannel } from './lib/channel.ts'
1213import { connectDevframeState } from './lib/devframe.ts'
14+ import { ruleCardId } from './lib/violation-view.ts'
1315
1416const SNIPPET = '<script type="module" src="…/inject.js"></script>'
1517const AUTOSCAN_KEY = 'devframes:plugin:a11y:autoscan'
@@ -223,7 +225,7 @@ export function App() {
223225
224226 // A dashboard/summary activation (or one with no target) just scrolls to top.
225227 if ( params . tab === 'dashboard' || ! route ) {
226- document . querySelector ( '. scroll' ) ?. scrollTo ( { top : 0 , behavior : 'smooth' } )
228+ document . querySelector ( '#a11y- scroll' ) ?. scrollTo ( { top : 0 , behavior : 'smooth' } )
227229 return
228230 }
229231
@@ -270,7 +272,7 @@ export function App() {
270272 }
271273
272274 return (
273- < div class = "app " >
275+ < div class = "flex flex-col h-full min-h-0 bg-base color-base font-sans " >
274276 < Header
275277 agentReady = { channel . agentReady ( ) }
276278 scanning = { channel . scanning ( ) }
@@ -280,48 +282,42 @@ export function App() {
280282 />
281283 < MetaLine
282284 url = { activeReport ( ) ?. url }
283- route = { activeReport ( ) ?. route }
284285 engine = { engine ( ) }
285286 backend = { devframe . backend }
286287 status = { devframe . status }
287288 />
288289
289- < p class = "visually-hidden " aria-live = "polite" > { announce ( ) } </ p >
290+ < p class = "sr-only " aria-live = "polite" > { announce ( ) } </ p >
290291
291- < div class = "scroll" >
292+ < div id = "a11y-scroll" class = "a11y- scroll flex-1 min-h-0 overflow-y-auto px-4 pb-5 " >
292293 < Switch >
293294 { /* No agent has announced itself on this origin yet. */ }
294295 < Match when = { ! channel . state ( ) && ! channel . agentReady ( ) } >
295- < div class = "state" >
296- < PlugIcon class = "state__glyph" />
297- < p class = "state__title" > No page connected</ p >
298- < p class = "state__body" >
299- Load the inspector agent in the app you want to check, then this
300- panel will list its accessibility issues live.
301- </ p >
302- < code class = "state__code" > { SNIPPET } </ code >
303- </ div >
296+ < EmptyState
297+ icon = "i-ph-plugs-duotone text-4xl"
298+ title = "No page connected"
299+ body = "Load the inspector agent in the app you want to check, then this panel will list its accessibility issues live."
300+ code = { SNIPPET }
301+ />
304302 </ Match >
305303
306304 { /* Agent present, first state not in yet. */ }
307305 < Match when = { ! channel . state ( ) } >
308- < div class = "state" >
309- < PlugIcon class = "state__glyph" />
310- < p class = "state__title" > Scanning the page…</ p >
311- < p class = "state__body" > Running axe-core against the connected document.</ p >
312- </ div >
306+ < EmptyState
307+ icon = "i-ph-plugs-duotone text-4xl"
308+ title = " Scanning the page…"
309+ body = " Running axe-core against the connected document."
310+ / >
313311 </ Match >
314312
315313 { /* Report in, nothing to flag. */ }
316314 < Match when = { totalFilterable ( ) === 0 } >
317- < div class = "state state--clean" >
318- < CheckCircle class = "state__glyph" />
319- < p class = "state__title" > No violations</ p >
320- < p class = "state__body" >
321- axe-core found nothing to flag across the tracked routes. Re-run
322- after changes to keep it that way.
323- </ p >
324- </ div >
315+ < EmptyState
316+ clean
317+ icon = "i-ph-check-circle-duotone text-4xl"
318+ title = "No violations"
319+ body = "axe-core found nothing to flag across the tracked routes. Re-run after changes to keep it that way."
320+ />
325321 </ Match >
326322
327323 { /* Single page: summary band + grouped violations. */ }
@@ -344,16 +340,19 @@ export function App() {
344340 < Show
345341 when = { shownViolations ( ) > 0 }
346342 fallback = { (
347- < div class = "state" >
348- < CheckCircle class = "state__glyph" />
349- < p class = "state__title" > Nothing matches the filter</ p >
350- < p class = "state__body" >
351- { totalFilterable ( ) }
352- { ' ' }
353- { totalFilterable ( ) === 1 ? 'rule' : 'rules' }
354- { ' at other severities. Clear the filter to see them.' }
355- </ p >
356- </ div >
343+ < EmptyState
344+ clean
345+ icon = "i-ph-check-circle-duotone text-4xl"
346+ title = "Nothing matches the filter"
347+ body = { (
348+ < >
349+ { totalFilterable ( ) }
350+ { ' ' }
351+ { totalFilterable ( ) === 1 ? 'rule' : 'rules' }
352+ { ' at other severities. Clear the filter to see them.' }
353+ </ >
354+ ) }
355+ />
357356 ) }
358357 >
359358 < ViolationList
0 commit comments