npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

kotsa-spot-game-widget

v1.0.8

Published

한국교통안전공단 교통안전 틀린그림찾기 — <script> 한 줄로 임베드하는 게임 위젯

Readme

한국교통안전공단 교통안전 틀린그림찾기 — 임베드 위젯

외부 사이트에 <script> 한 줄로 삽입하는 교통안전 틀린그림찾기 게임 위젯입니다. Vite 라이브러리 모드로 단일 JS 파일 하나(IIFE)로 빌드되며, Shadow DOM 으로 스타일이 완전히 격리되어 host 사이트와 서로 영향을 주지 않습니다.

기술 스택

  • Vite 8 (라이브러리 모드 · IIFE 단일 번들)
  • React 19 · TypeScript
  • Tailwind CSS v4 · shadcn/ui (radix-nova)
  • Shadow DOM 스타일 격리 — 별도 CSS 파일 없이 JS 한 개로 자체 완결

빌드

pnpm install
pnpm build      # → dist/kotsa-spot-the-difference.js (단일 파일)

개발 미리보기:

pnpm dev        # 외부 사이트를 흉내 낸 index.html 로 위젯 확인

npm 배포 · CDN(unpkg) 임베드

pnpm build      # dist/ 생성
npm publish     # prepublishOnly 가 build 를 다시 실행해 dist 를 최신화
  • package.jsonfiles: ["dist"] 로 배포 패키지에는 dist/ 만 포함됩니다. (.npmignore 가 있어 .gitignoredist 무시 규칙은 적용되지 않습니다.)
  • 배포 후에는 별도 호스팅 없이 unpkg 로 바로 임베드할 수 있습니다:
<script src="https://unpkg.com/[email protected]"></script>
  • 버전 고정(@1.0.0)을 권장합니다. 버전을 생략하면(.../kotsa-spot-game-widget) 항상 최신 버전을 받습니다. package.jsonunpkg 필드가 번들 파일을 가리키므로 경로를 적지 않아도 됩니다. (jsdelivr 도 동일)
  • npm 에 동일한 패키지 이름이 이미 있으면 게시할 수 없습니다 — 그럴 땐 name 을 스코프(@조직/kotsa-spot-game-widget)로 바꾸세요.

사용 방법 (외부 사이트에서 임베드)

1) 자동 삽입 — <main> 안에 append

스크립트만 넣으면 host 페이지의 <main> 태그 안에 게임이 자동으로 삽입됩니다.

<main></main>

<script src="https://unpkg.com/[email protected]"></script>

2) 특정 위치에 삽입

원하는 자리에 data-kotsa-game 요소를 두면 그 자리에 마운트됩니다. (이 속성을 가진 요소가 하나라도 있으면 <main> 자동 삽입은 건너뜁니다.)

<div data-kotsa-game></div>

<script src="https://unpkg.com/[email protected]"></script>

3) 수동 제어 (JavaScript API)

<div id="game-here"></div>

<script src="https://unpkg.com/[email protected]"></script>
<script>
  // mount 는 위젯을 제거하는 unmount 함수를 반환합니다.
  const unmount = KotsaSpotTheDifference.mount("#game-here");
  // unmount();
</script>

demo.html 을 빌드 후 브라우저로 열어 실제 임베드 동작을 확인할 수 있습니다.

게임 구성

  • 5단계, 단계마다 숨은 차이 5곳 (전체 25곳)
  • 제한 시간 · 점수 · 콤보 보너스 · 힌트 3회 · 단계별 결과 · 최종 등급(S~D)
  • 어린이 보호구역 / 전동킥보드 / 기차역 승강장 / 공항 보안검색 / 패러글라이딩
  • 시작 인트로 3종: 시작 화면 · 개인정보 수집 동의 · 정보 입력(호스트 폼 제출)

프로젝트 구조

src/
├─ embed.tsx                  # 라이브러리 진입점 (IIFE) — 자동 마운트 + 전역 API
├─ mount.tsx                  # Shadow DOM 마운트 로직 (스타일 격리)
├─ dev.tsx                    # 개발 서버 전용 진입점
├─ index.css                  # Tailwind v4 + 테마 + 게임 애니메이션 (?inline 로 번들 내장)
├─ components/
│  ├─ game/                   # 게임 화면·로직 UI
│  │  ├─ game-app.tsx          # 전체 흐름 (타이머·상태·토스트)
│  │  ├─ start-screen.tsx      # 시작 인트로 3종 (시작·개인정보 동의·정보 입력)
│  │  ├─ game-hud.tsx          # 점수·시간·힌트 HUD
│  │  ├─ difference-board.tsx  # 두 그림 보드 + 클릭 판정
│  │  ├─ image-panel.tsx       # 한 장의 그림 + 마커
│  │  ├─ stage-transition-overlay.tsx  # 단계 결과(포털 없는 자체 오버레이)
│  │  ├─ result-screen.tsx     # 최종 결과·등급
│  │  └─ game-toast.tsx        # 위젯 내부 전용 토스트
│  └─ site/kotsa-mark.tsx     # 엠블럼
└─ lib/
   ├─ stages.ts               # 스테이지 정의
   ├─ game-types.ts           # 공통 타입
   ├─ game-config.ts          # 점수 규칙·등급
   ├─ game-reducer.ts         # 게임 상태 머신
   ├─ hit-test.ts             # 클릭 정답 판정
   ├─ host-form.ts            # 호스트 사이트 입력 폼 채우기·제출
   └─ assets.ts               # asset/ 이미지 인라인

동작 원리 — 스타일 격리

  • 위젯은 대상 요소에 Shadow DOM 을 붙이고 그 안에 렌더링합니다. host 사이트의 CSS 가 위젯에 스며들지 않고, 위젯 CSS 도 host 로 새어 나가지 않습니다.
  • index.css?inline 로 import 되어 JS 번들 안에 문자열로 내장됩니다. (배포 파일은 JS 하나뿐 — 별도 CSS 링크 불필요)
  • Tailwind v4 의 @property 규칙은 문서 전역 등록이 필요하므로 document.head 로 한 번만 옮겨 주입하고, 나머지 규칙은 Shadow DOM 안에 둡니다.
  • 반응형은 뷰포트가 아닌 위젯 컨테이너 너비(CSS container query)를 기준으로 동작하므로, 좁은 칼럼에 임베드해도 레이아웃이 자연스럽게 맞춰집니다.

그림·차이점 교체하기

장면은 asset/stageN-a.png(원본) · asset/stageN-b.png(다른그림) 이미지를 사용합니다. 그림을 바꾸려면 asset/ 의 파일을 교체하고, src/lib/stages.tsdifferences 좌표(x·y·r, 이미지 기준 %)를 새 이미지에 맞게 잡으면 됩니다. 개발 모드(pnpm dev)에서 그림을 클릭하면 콘솔에 클릭 좌표가 출력되므로 그 값으로 보정하면 됩니다. (이미지는 빌드 시 base64 로 번들에 인라인됩니다 — asset/README.md 참고.)


본 프로젝트는 교통안전 교육용 데모입니다.