한눈에 보기
- 무엇: Claude Code 화면 오른쪽 아래 링이 보여 주는 5시간 사용 한도를, 탭을 열지 않고도 Firefox 고정 탭 줄에서
업무 48% ⏱3h ▮처럼 보는 방법입니다. - 언제 쓰나: Multi-Account Containers로 Claude 계정 여러 개를 컨테이너마다 나눠 쓰고, Claude Code 탭을 고정 탭으로 두는 경우입니다.
- 핵심 방법: 작은 확장이 링 값을 읽어 탭 제목 앞에
▄ 48% ⏱3h를 붙이고,userChrome.css가 탭의label속성을 속성 선택자로 읽어 숫자와 배터리 칸을 그립니다. 페이지가 제목을 되돌리면 MutationObserver 콜백 안에서 즉시 다시 붙여야 깜빡이지 않습니다.
확인한 환경: Windows, Firefox 140 이상, Multi-Account Containers, Claude Code 웹 화면(2026년 10월 29일) · 처음 쓴 날 2026-11-03 · 마지막 확인 2026-10-29
Claude 계정을 여러 개 쓰다 보면 “지금 어느 계정이 한도에 가까운지”를 자주 확인하게 됩니다. Claude Code 웹 화면에는 오른쪽 아래에 작은 원형 링이 있고, 마우스를 올리면 “5시간 한도의 70%”처럼 5시간 사용 한도를 알려 줍니다. 그런데 이 값을 보려면 그 계정의 탭을 열고 링에 마우스를 올려야 합니다. 이 글은 UNIT5 개발팀이 이 값을 Firefox 고정 탭에 배터리처럼 띄우면서 만든 구조와, 중간에 겪은 깜빡임·CSS 함정을 정리한 것입니다. 고정 탭에 컨테이너 이름을 표시하는 기본 설정은 파이어폭스 탭 관리 가이드에서 먼저 다뤘고, 이 글은 그 위에 숫자를 더합니다.
1. 완성하면 고정 탭이 이렇게 보입니다
완성된 고정 탭은 컨테이너 아이콘과 이름 옆에 사용률, 초기화까지 남은 시간, 배터리 모양 칸을 보여 줍니다. 배터리 칸은 사용률만큼 아래에서부터 컨테이너 색으로 차고, 남은 시간은 ⏱3h, ⏱50m처럼 단계로 줄어듭니다. 고정 탭은 원래 제목을 그리지 않기 때문에, 확장이 붙인 원래 제목은 마우스를 올렸을 때 나오는 툴팁에만 보입니다.

필요한 것은 두 가지입니다. 하나는 링 값을 읽어 탭 제목에 붙이는 작은 Firefox 확장(content script)이고, 다른 하나는 그 제목을 읽어 고정 탭을 그리는 userChrome.css입니다. 확장은 Manifest V2로 만들었고, 권한은 contextualIdentities, cookies, storage, https://claude.ai/*을 썼습니다.
2. Claude Code 5시간 사용 한도 값은 어디서 읽나요?
Claude Code의 5시간 사용 한도는 화면 오른쪽 아래 링의 SVG 속성과, 링을 감싼 버튼의 aria-label에서 읽을 수 있습니다. 오른쪽 패널의 “컨텍스트 23%”는 대화 길이를 뜻하는 다른 값이라 헷갈리지 않아야 합니다. 2026년 10월 29일 화면에서 링은 아래와 같은 모양이었습니다.
<svg width="14" height="14" viewBox="0 0 14 14" class="-rotate-90" aria-hidden="true">
<circle cx="7" cy="7" r="6" fill="none" stroke-width="2" stroke="var(--cds-alpha-3)" data-ring-track=""></circle>
<circle cx="7" cy="7" r="6" fill="none" stroke-width="2"
stroke-dasharray="37.69911184307752" stroke-dashoffset="29.028316119169688"
stroke-linecap="round" stroke="var(--cds-fill-accent)" data-ring-arc=""></circle>
</svg>
stroke-dasharray는 원 둘레(2π × 6 = 37.699)이고,stroke-dashoffset은 아직 칠하지 않은 길이입니다.- 사용률은
1 − offset ÷ 둘레입니다. 위 예시는 1 − 29.03 ÷ 37.70 ≈ 0.23, 곧 23%입니다. - 링을 감싼 버튼에는
aria-label="사용량: 5시간 한도의 44% 오후 7:40에 초기화"같은 문장이 늘 들어 있습니다. 초기화 시각은 툴팁을 띄우지 않고 여기서 읽습니다.
content script는 data-ring-arc 요소 하나에만 MutationObserver를 붙입니다. 문서 전체를 감시하면 Claude가 답을 스트리밍하는 동안 콜백이 쉴 새 없이 깨어나서 부하가 커지기 때문입니다.
3. 컨테이너 이름 대신 탭 제목으로 고정 탭에 값을 보내는 이유
고정 탭에 값을 보내는 통로는 컨테이너 이름이 아니라 탭 제목입니다. userChrome.css는 컨테이너 이름을 읽을 방법이 없고, 탭 요소에는 usercontextid 번호와 색·아이콘 클래스만 붙어 있기 때문입니다. 반면 탭 요소의 label 속성은 탭 제목을 그대로 따라가서, 속성 선택자로 읽을 수 있습니다.
처음에는 확장의 contextualIdentities API로 컨테이너 이름 자체를 업무 ▄ 48%처럼 바꿨습니다. 확장 팝업과 주소창의 컨테이너 표시에는 새 이름이 보였지만, 고정 탭에는 계속 “업무”만 나왔습니다. 고정 탭의 “업무”는 userChrome.css에 content: "업무"로 직접 적어 둔 글자였기 때문입니다. 컨테이너 이름에는 그림을 넣을 수도 없습니다. 이름은 글자만 받고, 아이콘은 Firefox 내장 13종 가운데서만 고를 수 있습니다. 그래서 원형 링 대신 유니코드 세로 막대 ▁▂▃▄▅▆▇█(8단계)를 쓰기로 했습니다.

userChrome.css: 이름 옆 숫자와 배터리 칸
이름 규칙은 원래 쓰던 줄 끝에 var() 두 개만 더합니다. 배터리 칸은 ::after를 이미 이름에 쓰고 있어서, ::before를 order: 1로 맨 뒤로 보내 그립니다.
/* 이름 규칙: 원래 줄 끝에 var()만 추가 (2 = 업무 컨테이너 예시) */
.tabbrowser-tab[pinned][usercontextid="2"] .tab-content::after {
content: "업무" var(--ctx-pct, "") var(--ctx-time, "");
}
/* 배터리 칸: ::before를 order로 맨 뒤에 */
.tabbrowser-tab[pinned][usercontextid] .tab-content::before {
content: ""; order: 1; display: none; width: 7px; height: 13px;
box-sizing: border-box; padding: 1px; border-radius: 2px;
border: 1px solid var(--identity-icon-color);
background: linear-gradient(to top, var(--identity-icon-color) var(--ctx-fill, 0%),
transparent var(--ctx-fill, 0%)) content-box;
}
/* 막대가 붙은 탭에서만 칸을 보이게 */
.tabbrowser-tab[pinned][usercontextid]:is([label^="▁"], [label^="▂"], [label^="▃"], [label^="▄"],
[label^="▅"], [label^="▆"], [label^="▇"], [label^="█"]) .tab-content::before {
display: block;
}
/* 0~100% 규칙 101개 가운데 하나 */
.tabbrowser-tab[label^="▄ 48%"] { --ctx-fill: 48%; --ctx-pct: " 48%"; }
101개 규칙은 손으로 쓰지 않고 짧은 파이썬으로 만듭니다. 막대 글자는 12.5%마다 한 단계씩 올라가서, 48%는 네 번째 막대 ▄가 됩니다. %까지 선택자에 넣어야 ▁ 1%가 ▁ 10%에 잘못 맞지 않습니다.
for p in range(101):
g = chr(0x2581 + min(7, int(p // 12.5)))
print(f'.tabbrowser-tab[label^="{g} {p}%"] {{ --ctx-fill: {p}%; --ctx-pct: " {p}%"; }}')
4. 탭 제목이 깜빡이거나 활성 탭에서 사라질 때
탭 제목에 접두어를 붙이는 방식의 가장 큰 적은 페이지 자신입니다. Claude 페이지는 자기 제목을 주기적으로 다시 쓰고, 그때마다 확장이 붙인 접두어가 지워집니다. 처음에는 이 때문에 고정 탭의 숫자와 칸이 “딸깍딸깍” 깜빡였고, 고친 뒤에는 탭을 눌러 활성화하면 숫자와 칸이 사라지는 문제가 생겼습니다.

깜빡임: 0.15초 늦게 다시 붙였기 때문
깜빡임은 다시 붙이는 시점이 늦어서 생겼습니다. 처음에는 제목 변경을 감지하고 0.15초 debounce 뒤에 접두어를 다시 붙였는데, 그 사이 Firefox가 원래 제목을 탭에 반영해서 칸이 사라졌다가 다시 나타났습니다. MutationObserver 콜백은 DOM 표준에서 마이크로태스크로 전달되므로, 콜백 안에서 즉시 다시 붙이면 페이지가 제목을 바꾼 같은 작업 단위 안에서 접두어가 돌아옵니다. 우리가 겪은 바로는 Firefox가 제목 변경을 다음 작업 단위에서 탭에 반영해서, 탭 입장에서는 제목이 바뀐 적이 없게 됩니다.

titleWatcher = new MutationObserver(() => {
observeTitle(); // <title> 요소가 교체됐을 수 있음
const prefix = titlePrefix(); // 예: "▄ 48% ⏱3h "
if (!prefix || document.title === prefix + document.title.replace(TITLE_RE, "")) return;
// 0.1초 안에 30번 넘게 되돌려지면 무한 반복 → 30초 쉼
const now = performance.now();
burst = burst.filter(t => now - t < 100);
burst.push(now);
if (burst.length > 30) { burst = []; pausedUntil = Date.now() + 30000; setTimeout(applyTitle, 30050); }
applyTitle(); // 같은 마이크로태스크 안에서 다시 붙임
});
활성 탭에서만 사라짐: 무한 반복 방지 기준이 너무 엄격했기 때문
배경 탭은 멀쩡한데 탭을 활성화하면 숫자와 칸이 사라진 원인은 무한 반복 방지 장치였습니다. 처음 기준은 “1초에 50번 넘게 되돌려지면 영구 포기”였고, 콘솔에는 확장이 남긴 [ctx-bar] 페이지가 제목 표시를 계속 되돌려서 이 탭에서는 제목 표시를 멈춰요. 경고가 찍혔습니다. 우리가 겪은 바로는 화면에 보이는 탭에서 Claude가 작업 중에 제목을 매 프레임 다시 쓰고, 배경 탭에서는 브라우저가 그런 갱신을 늦춰서 배경 탭만 기준에 걸리지 않았습니다.
그래서 “같은 순간 안에서 끝없이 되돌리는 것”만 무한 반복으로 보도록 바꿨습니다. 기준은 0.1초에 30번 초과이고, 걸려도 영구 포기하지 않고 30초 뒤에 다시 시도합니다. 모의 환경(jsdom)에서 매 프레임 재설정을 재현했을 때, 예전 기준은 약 0.5초 만에 포기했고 새 기준은 계속 표시를 유지했습니다.
5. userChrome.css에서 겪은 함정 세 가지
제목 쪽이 안정된 뒤에도 CSS에서 세 번 막혔습니다. 셋 다 문법 오류처럼 보이지 않아서 찾기 어려웠습니다.
① 16진 이스케이프 뒤의 공백이 사라진다
[label^="\2581 0%"]로 쓴 규칙이 ▁ 0%로 시작하는 제목에 맞지 않았습니다. CSS Syntax Level 3에 따르면 16진 이스케이프 바로 뒤의 공백 하나는 “이스케이프가 끝났다”는 표시로 소비됩니다. 그래서 "\2581 0%"는 공백 없는 ▁0%가 됩니다. 막대 글자를 파일에 UTF-8 그대로 쓰거나, 공백을 두 칸 쓰면 해결됩니다.
② 고정 탭 아래에 녹색 점이 계속 생긴다
Firefox는 고정 탭의 제목이 바뀌면 탭에 titlechanged 속성을 달고 .tab-content에 알림 점을 그립니다. 사용률이 바뀔 때마다 제목이 바뀌니 점이 계속 생겼습니다. 다른 사이트 고정 탭의 알림은 살려 두고, 막대가 붙은 탭에서만 점을 숨깁니다.
.tabbrowser-tab[pinned][usercontextid][titlechanged]:is([label^="▁"], [label^="▂"], [label^="▃"],
[label^="▄"], [label^="▅"], [label^="▆"], [label^="▇"], [label^="█"]) .tab-content {
background-image: none !important;
}
③ var() 하나가 틀리면 이름과 아이콘이 통째로 사라진다
CSS를 고치던 중 고정 탭의 이름과 컨테이너 아이콘이 한꺼번에 사라졌습니다. var()가 들어간 선언은 파싱할 때가 아니라 값을 계산할 때 유효한지 검사합니다. MDN 설명처럼 그때 값이 무효면 그 속성은 상속값이나 초기값이 되고, 앞에 쓴 다른 선언(content: "")으로 돌아가지도 않습니다. content의 초기값 normal은 ::after에서 none이 되므로 의사 요소 자체가 생기지 않고, 거기에 배경으로 그리던 아이콘까지 같이 사라집니다.

이때는 수정하다 원래 파일의 윗부분이 지워진 일도 겹쳤습니다. 원래 규칙, 이름 줄, 막대 블록을 합친 userChrome.css 전체를 다시 만들어 해결했습니다. 다시 만든 뒤에는 단순 문법 검사(css-tree)에 더해, 탭 상태별로 var()를 치환한 content 값이 유효한지도 계산해서 확인했습니다. var()에는 위 예시처럼 항상 대체값(var(--ctx-pct, ""))을 주는 편이 안전합니다.
6. 초기화까지 남은 시간을 고정 탭에 붙이는 법
남은 시간은 링 버튼의 aria-label에 있는 초기화 시각으로 계산합니다. “오후 7:40에 초기화”에서 “오전/오후 h:mm”을 읽어 다음에 오는 그 시각을 구하고(이미 지났으면 다음 날), 지금부터 남은 시간을 올림해서 단계로 나눕니다.
- 50분이 넘게 남으면 시간 단위로
5h,4h,3h,2h,1h입니다. - 50분 이하로 남으면 10분 단위로
50m,40m,30m,20m,10m입니다. - 제목에는
⏱3h처럼 붙이고, CSS는[label*="⏱3h "]같은 규칙 10개로--ctx-time을 정합니다. 단계가 바뀔 때만 제목이 바뀌어서 5시간 동안 10번 안팎입니다.
.tabbrowser-tab[label*="⏱3h "] { --ctx-time: " \23F1\FE0E 3h"; }
시계는 \23F1\FE0E로 씁니다. 뒤의 \FE0E(VS15)는 컬러 이모지 대신 글자 모양으로 그리게 해서 시계가 컨테이너 색을 따라가게 합니다. 여기서는 5장의 이스케이프 규칙이 오히려 도움이 됩니다. \FE0E 뒤의 공백 하나가 소비돼서 화면에는 ⏱3h처럼 붙어 나옵니다. 남은 시간 계산은 한국어 화면(“오후 7:40에 초기화”)에서만 확인했고, 영어 화면 형식은 확인하지 않았습니다.
7. 설치 방법과 미리 알아둘 한계
about:config에서toolkit.legacyUserProfileCustomizations.stylesheets를true로 바꿉니다.userChrome.css는 Firefox를 다시 시작해야 반영됩니다. 파일 만드는 법은 파이어폭스 탭 관리 가이드의 3장에 있습니다.- 확장은
about:debugging의 임시 부가 기능으로 바로 시험할 수 있지만, 임시 부가 기능은 다시 시작하면 사라집니다. - 계속 쓰려면 서명을 받아야 합니다. Firefox 정식판과 베타판은 서명된 부가 기능만 설치할 수 있습니다. 우리는
npx web-ext sign --channel=unlisted(web-ext 10.8.0)로 AMO 비공개(자체 배포) 서명을 받았습니다. API 키와 비밀값은 AMO 개발자 허브에서 발급받아 넣습니다.
서명은 Waiting for validation..., Waiting for approval...을 거쳐 몇 분 안에 끝났고, 서명된 xpi가 web-ext-artifacts 폴더에 생겼습니다. 실행 중 나오는 npm warn deprecated 경고는 서명과 상관없습니다. 기다리다 시간 초과로 끝나면 같은 버전을 다시 올릴 수 없으니, AMO 개발자 허브의 버전 목록에서 서명된 파일을 내려받습니다.
이 방법은 공식 API가 아닌 화면 구조에 기대고 있어서, 아래 경우에 깨질 수 있습니다.
- claude.ai 화면 구조가 바뀌어
data-ring-arc요소나aria-label문장이 달라지면 값을 읽지 못합니다. - 탭의
label·titlechanged속성과.tab-content구조는 Firefox 내부 구조라서 버전이 바뀌면 달라질 수 있습니다. 표시가 안 되면 브라우저 도구 상자(Ctrl+Shift+Alt+I)로 탭 요소를 검사해 봅니다. - Firefox가 제목 변경을 다음 작업 단위에서 탭에 반영한다는 점과, Claude가 활성 탭에서 제목을 매 프레임 다시 쓴다는 점은 실제 증상과 모의 테스트로 확인했고 Firefox 소스 코드나 직접 측정으로는 확인하지 않았습니다.
자주 묻는 질문
Claude Code 5시간 사용 한도는 어디서 볼 수 있나요?
Claude Code 웹 화면 오른쪽 아래의 작은 원형 링에 마우스를 올리면 “5시간 한도의 70%”처럼 나옵니다. 오른쪽 패널의 “컨텍스트” 퍼센트는 대화 길이를 뜻하는 다른 값입니다. 2026년 10월 29일 화면 기준입니다.
userChrome.css에서 컨테이너 이름을 읽을 수 있나요?
읽을 수 없습니다. 탭 요소에는 usercontextid 번호와 색·아이콘 클래스만 있어서, 이름은 CSS에 직접 적어야 합니다. 바뀌는 값을 보여 주려면 확장이 탭 제목을 바꾸고 CSS가 탭의 label 속성을 속성 선택자로 읽는 방법을 씁니다.
CSS 이스케이프 \2581 뒤의 공백은 왜 사라지나요?
CSS 문법은 16진 이스케이프 바로 뒤의 공백 하나를 이스케이프의 끝 표시로 보고 소비합니다. 그래서 “\2581 0%”는 “▁0%”가 됩니다. 글자를 UTF-8 그대로 쓰거나 공백을 두 칸 쓰면 됩니다.
var()를 넣었더니 ::after가 통째로 사라지는 이유는?
var()가 들어간 선언은 값을 계산할 때 유효성을 검사하고, 무효면 속성이 초기값이 됩니다. content의 초기값 normal은 ::after에서 none이 되어 의사 요소가 생기지 않습니다. var(–x, “”)처럼 대체값을 주면 막을 수 있습니다.
참고 자료
- DOM Standard — Mutation observers: MutationObserver 콜백이 마이크로태스크로 전달된다는 근거입니다.
- MDN — Using CSS custom properties: var() 치환이 무효일 때 상속값이나 초기값이 쓰인다는 설명입니다.
- CSS Syntax Level 3 — consume an escaped code point: 16진 이스케이프 뒤 공백 하나가 소비되는 규칙입니다.
- Firefox Extension Workshop — Signing and distribution overview: 정식판에 설치하려면 서명이 필요하고, 자체 배포(unlisted) 서명과 web-ext sign을 쓸 수 있다는 안내입니다.
함께 보면 좋은 글
- 파이어폭스 탭 관리 완벽 가이드: 컨테이너·탭 그룹 동기화, 고정 탭 컨테이너 표시, 메모리 줄이기 — 이 글의 바탕이 된 고정 탭 컨테이너 아이콘·이름 표시와 userChrome.css 만드는 법입니다.
- GitHub 문서를 NotebookLM에서 활용하는 가장 간단한 방법 — 또 다른 AI 도구를 개발 문서와 함께 쓰는 요령입니다.

답글 남기기