Click-to-paint 가 metric. User 가 toolbar icon click 한 순간부터 popup 이 첫 의미 있는 frame 렌더하는 순간까지, latency 알아채기 전 ~200 ms 가짐. 그 예산 사는 세 습관:
Sync 렌더, async refine. Placeholder 값 ("0 clips" 나 "—") 으로 layout 즉시 표시. chrome.storage.local.get resolve 되면서 실제 data 로 교체. User 가 구조 먼저, 숫자 그 다음 봄.
CSS-in-JS 나 큰 스타일 framework 피하기.<style> 태그의 vanilla CSS (또는 작은 외부 sheet) 가 JS 돌기도 전에 렌더. Paint 전 hydrate 하는 framework 가 예산 깰 수 있음.
비싼 helper lazy-load. Clipboard helper 나 markdown renderer 있으면, module top level 가 아닌 그게 필요한 click handler 안에서 import.
Focus 와 keyboard
Popup 이 keyboard reachable: user 가 Alt+Shift+T (또는 configured shortcut) 누르고 Tab 으로 navigate. 세 규칙:
첫 focusable 요소가 열릴 때 focus 받음. 첫 버튼 대신 search input 에 focus 원하면 input 에 autofocus 추가. Tab 으로 확인.
Escape 가 popup 우아하게 닫아야 함 (Chrome 이 이미 처리, 그러나 keydown intercept 하면 Esc 막지 안 함).
Popup 이 list 포함하면, row 가 keyboard-navigable: 각 row 가 tabindex="0" 과 명시적 Enter handler 가짐. 아니면 user 가 마우스로만 가능.
Popup 당 single purpose
Popup 이 작아. Settings / library / quick-action 을 한 tabbed view 에 다 넣고 싶은 충동 저항. 규율 잡힌 ClipDeck popup:
맨 위: 두 줄 status ("5 clips today, 0 this hour").
가운데: 버튼으로 1–3 primary action (Save current selection / Open clip list / Pause on this site).
맨 아래: secondary link — "Settings" / "Help" / "View on GitHub". Plain text link, 버튼 아님. 시각 무게를 primary action 에 유지.
User 가 뭔가 browse 필요하면 side panel. 뭔가 configure 필요하면 options page (Track 6). Popup 은 one-shot intent 용.
열려 있는 동안 state update
Popup 이 몇 초 열려 있으면, side panel 처럼 chrome.storage.onChanged 구독. User 가 다른 tab 에서 뭔가 바꿀 수 있음; popup 이 즉시 반영해야 함. Popup close 에 unsubscribe 자동 — popup JavaScript context 죽고, listener 도 같이 죽음.
Popup = one shot, one purpose. 구조 먼저 렌더, data 로 refine. Sub-200 ms 가 즉각 느껴짐; sub-100 ms 가 native 느낌. Browse 나 configure 는 다른 데로.
Popup auto-close 함정. Popup 안에서 chrome.tabs.create, chrome.windows.create, 또는 window 여는 어떤 chrome.action API 든 호출하면 popup 즉시 닫힘. 가끔 그게 원하는 거 (action 완료, 자기 dismiss); 가끔 안 (user 가 두 가지 하고 싶었음). Two-step 상호작용에는 두 번째 step 미루든가 둘 다 side panel 로 옮기든가.
Code
popup.html — primary action 셋, secondary link 둘·html
clipdeck/popup.html 을 첫 번째 code block 으로, clipdeck/popup.js 를 두 번째로 교체. Extension reload. Toolbar icon click — popup 이 placeholder 0 으로 즉시 렌더, 다음 실제 count 가 ms 안에 들어옴. 세 primary 버튼 테스트: Save current selection (먼저 텍스트 선택, 후 click) 이 clip 추가; Open clip list 가 side panel 열고 popup 닫음; Pause on this site 는 마지막에서 두 번째 lesson 에서 wire, 지금은 그냥 있는지 확인. Popup 열기, 다른 window 에서 Ctrl+Shift+K 로 clip 저장, 다시 전환 — popup count 가 re-open 없이 update.
Hint
Placeholder 0 이 실제 숫자로 교체 안 되면 refreshStats() 가 throw 중 — popup DevTools (popup 우클릭 → Inspect → Console) 열어 error 확인. 흔한 게 chrome:// special-page 케이스에서 new URL(tab.url) 동작하지만 host filter 가 매칭 0 반환해 siteCount 가 명백한 error 없이 0 유지. Popup 이 예상보다 넓으면 padding 이 명시적 width 안으로 먹도록 body 에 box-sizing: border-box 설정 확인. Save current selection 이 실제 web page 에서도 alert 하면 content script 가 load 안 됨 — content.js 가 content_scripts.matches 안에 있는지 확인.
Progress
Progress is local-only — sign in to sync across devices.