From 6ab3e146d9275c443f464503708f49ed17347fd9 Mon Sep 17 00:00:00 2001 From: Justin Levine <20596508+justinlevinedotme@users.noreply.github.com> Date: Wed, 22 Apr 2026 14:16:55 -0700 Subject: [PATCH 1/4] fix(registry): swap activity-graph to contributions API, add Radix tooltips - Replace fragile HTML scraping in lib/github.ts with the github-contributions-api (jogruber.de/v4) for structured JSON - Replace native title attributes with Radix Tooltip for styled hover popovers showing date and count on each cell - Add radix-ui dependency to registry.json - Update docs notes to reflect new API and tooltip behavior --- .pi/skills/jalco-ui-registry/SKILL.md | 97 +++++++++++++++ .../references/component-workflow.md | 113 +++++++++++++++++ .../references/docs-patterns.md | 105 ++++++++++++++++ .../references/registry-patterns.md | 117 ++++++++++++++++++ .../jalco-ui-registry/references/repo-map.md | 113 +++++++++++++++++ .../references/review-checklist.md | 114 +++++++++++++++++ .../references/variants-and-api.md | 114 +++++++++++++++++ .../docs/components/activity-graph.mdx | 6 +- apps/docs/registry.json | 3 + .../activity-graph/activity-graph.tsx | 47 +++++-- .../registry/activity-graph/lib/github.ts | 78 +++++------- 11 files changed, 843 insertions(+), 64 deletions(-) create mode 100644 .pi/skills/jalco-ui-registry/SKILL.md create mode 100644 .pi/skills/jalco-ui-registry/references/component-workflow.md create mode 100644 .pi/skills/jalco-ui-registry/references/docs-patterns.md create mode 100644 .pi/skills/jalco-ui-registry/references/registry-patterns.md create mode 100644 .pi/skills/jalco-ui-registry/references/repo-map.md create mode 100644 .pi/skills/jalco-ui-registry/references/review-checklist.md create mode 100644 .pi/skills/jalco-ui-registry/references/variants-and-api.md diff --git a/.pi/skills/jalco-ui-registry/SKILL.md b/.pi/skills/jalco-ui-registry/SKILL.md new file mode 100644 index 0000000..bdb9a34 --- /dev/null +++ b/.pi/skills/jalco-ui-registry/SKILL.md @@ -0,0 +1,97 @@ +--- +name: jalco-ui-registry +description: Create, review, and document jalco ui registry items with shadcn-style ergonomics, strong variants, and docs-to-registry alignment. Use for adding jalco ui components or blocks, updating registry.json, writing component docs, reviewing API and composition quality, or navigating the jalco-ui docs and registry structure. Use proactively when working in /Users/justin/Documents/Github/jalco-ui or when the user asks to create a component, improve variants, write docs, or audit registry readiness. +--- + +# jal-co/ui Registry + +Repo-aware guidance for building polished jalco ui registry items and documentation. + +## When to Use + +Use this skill when working in: +- `/Users/justin/Documents/Github/jalco-ui` + +Use it for: +- creating a new jalco ui component or block +- reviewing a registry item for readiness +- updating `registry.json` +- writing or revising docs pages under `app/docs/components/*` +- deciding whether a component needs variants +- aligning implementation, docs copy, exports, and registry metadata +- navigating the repo's docs and registry structure + +## Read First + +Start with these repository files: +1. `/Users/justin/Documents/Github/jalco-ui/AGENTS.md` +2. `/Users/justin/Documents/Github/jalco-ui/registry.json` +3. `/Users/justin/Documents/Github/jalco-ui/README.md` +4. `/Users/justin/Documents/Github/jalco-ui/app/docs/page.mdx` +5. `/Users/justin/Documents/Github/jalco-ui/app/docs/installation/page.mdx` + +Then route by task: +- Need repo orientation or example files → `references/repo-map.md` +- Creating a new component or block → `references/component-workflow.md` +- Designing variants and public API → `references/variants-and-api.md` +- Writing or revising docs → `references/docs-patterns.md` +- Editing registry metadata → `references/registry-patterns.md` +- Auditing existing work → `references/review-checklist.md` + +## Core Rules + +- Follow jalco ui's repo conventions before introducing new patterns. +- Reuse existing registry and docs patterns before inventing new structure. +- Keep components installable, readable, and easy to adapt. +- Prefer composability over boolean-prop-heavy APIs. +- Add variants when they represent real reusable visual states, not speculative flexibility. +- Treat docs as part of the feature, not a follow-up. +- Keep naming aligned across folder names, exports, docs titles, slugs, and `registry.json` entries. +- Use `"use client"` only when interactivity actually requires it. +- Prefer realistic examples over placeholder content. +- Follow jalco ui's compact comment style for Jalco-authored public files: useful file headers when appropriate, minimal inline comments, and no decorative AI-style section banners. +- For public component entry files, headers should usually include the component name, a one-sentence description, and key props when helpful. +- Do not mass-retrofit copied, upstream, generated, or template-derived files unless they are being meaningfully rewritten. + +## Best Existing Anchors + +Use these as primary examples unless a closer match exists: +- Registry implementation: + - `/Users/justin/Documents/Github/jalco-ui/registry/github-stars-button/github-stars-button.tsx` + - `/Users/justin/Documents/Github/jalco-ui/registry/github-stars-button/github-button-group.tsx` + - `/Users/justin/Documents/Github/jalco-ui/registry/waitlist-card/waitlist-card.tsx` + - `/Users/justin/Documents/Github/jalco-ui/registry/code-line/code-line.tsx` +- Docs pages: + - `/Users/justin/Documents/Github/jalco-ui/app/docs/components/github-stars-button/page.tsx` + - `/Users/justin/Documents/Github/jalco-ui/app/docs/components/github-button-group/page.tsx` + - `/Users/justin/Documents/Github/jalco-ui/app/docs/components/waitlist-card/page.tsx` + - `/Users/justin/Documents/Github/jalco-ui/app/docs/components/code-line/page.tsx` + +## Expected Output + +When creating or updating jalco ui work, usually produce or update: +- one or more files under `registry//` +- a matching `registry.json` entry +- a docs page under `app/docs/components//page.tsx` when the item is public-facing +- a catalog card preview at `components/docs/previews/.tsx` +- a sidebar nav entry in `lib/docs.ts` +- installation and usage copy aligned with the registry payload URL +- notes about accessibility, API quality, and any tradeoffs + +After creating or modifying a card preview file, run `pnpm previews:generate` to update the auto-generated import map. + +## Validation + +Before considering work done: +- confirm work is on a feature branch (`feat/`), not main +- confirm the item type and file paths are correct +- confirm metadata and docs descriptions align +- confirm dependencies and registryDependencies are justified +- confirm variants are intentional and documented when exposed +- confirm docs include realistic preview, install, and usage guidance +- confirm a catalog card preview exists at `components/docs/previews/.tsx` with key variants +- confirm the sidebar nav entry exists in `lib/docs.ts` with `badge: "New"` and `badgeAdded` date +- confirm screenshots exist at `public/previews/-dark.png` and `-light.png` +- confirm `pnpm registry:build` and `pnpm build` pass +- confirm the component feels shadcn-style: composable, accessible, and easy to own +- open a PR using the component template (`.github/PULL_REQUEST_TEMPLATE/component.md`) diff --git a/.pi/skills/jalco-ui-registry/references/component-workflow.md b/.pi/skills/jalco-ui-registry/references/component-workflow.md new file mode 100644 index 0000000..69f279c --- /dev/null +++ b/.pi/skills/jalco-ui-registry/references/component-workflow.md @@ -0,0 +1,113 @@ +# Component Workflow + +Use this workflow when creating or substantially revising a Jalco UI registry item. + +## Step 1: Understand the request + +Clarify: +- is this a component, block, docs utility, or multi-file registry item? +- what problem does it solve? +- who installs it and customizes it? +- does it need to be interactive? +- does it belong in Jalco UI at all? + +If the request is vague, identify the smallest useful artifact that feels publishable. + +## Step 2: Read the repo before building + +Read: +- `/Users/justin/Documents/Github/jalco-ui/AGENTS.md` +- `/Users/justin/Documents/Github/jalco-ui/registry.json` +- `/Users/justin/Documents/Github/jalco-ui/package.json` + +Then inspect: +- the closest matching file in `registry/` +- the closest matching docs page in `app/docs/components/` + +## Step 3: Choose the correct registry shape + +Common Jalco UI patterns: +- simple single-file item → `registry//.tsx` +- multi-file item → colocate `components/`, `lib/`, or `hooks/` under `registry//` +- primitive dependency reuse → import from `@/registry/ui/*` when appropriate + +Prefer the smallest structure that still feels clean and installable. + +## Step 4: Design the API + +Before coding, decide: +- whether the component should be server or client +- what props are truly public API +- whether variants are justified +- whether customization is best expressed as composition, children, slots, className, or CVA variants + +Do not add props just because they might be useful someday. + +## Step 5: Implement like a public registry item + +Target quality bar: +- readable source +- minimal dependencies +- strong defaults +- easy to own after installation +- accessibility considered by default +- realistic markup and copy +- consistent Jalco UI comment and header style + +Use `"use client"` only when required by state, effects, browser APIs, event-heavy interactivity, or client-only dependencies. + +Comment rules during implementation: +- use a compact top-of-file header for Jalco-authored public-facing source files when attribution or dependency context is useful +- for public component entry files, include the component name, a one-sentence description, and key props when helpful +- prefer the Jalco UI header shape: `jalco-ui`, component name, `by Justin Levine`, `ui.justinlevine.me`, then optional description, props, dependencies, inspiration, and runtime notes +- keep inline comments minimal and useful +- do not add decorative separator banners or AI-style section headings +- do not mass-retrofit copied, upstream, generated, or template-derived files unless they are being meaningfully rewritten + +## Step 6: Add or update `registry.json` + +For each public item, ensure: +- `name` matches the folder and install URL +- `type` is correct +- `title` is human-readable +- `description` is concise and docs-aligned +- `dependencies` lists package dependencies only when required +- `registryDependencies` lists upstream shadcn registry items when required +- `files` paths and types are correct +- `categories` are used when they improve organization + +## Step 7: Write the docs page + +For a public-facing component, create or update: +- `/Users/justin/Documents/Github/jalco-ui/app/docs/components//page.tsx` + +Typical docs flow: +1. metadata +2. title + concise description +3. dependency badges +4. preview +5. installation command +6. usage import + basic example +7. examples / features / customization / API / notes as justified + +Only include sections that improve adoption. + +## Step 8: Review before calling it done + +Check: +- Does this feel installable and useful? +- Is the API smaller and clearer than the first draft? +- Are variants justified and shown in docs? +- Is the docs copy polished and aligned with metadata? +- Is the implementation consistent with existing Jalco UI work? + +## Common anti-patterns + +Avoid: +- boolean-prop-heavy APIs +- variants without real visual meaning +- generic placeholder examples +- unnecessary client components +- docs that repeat obvious implementation details +- registry metadata that drifts from actual behavior +- over-abstracting before multiple real use cases exist diff --git a/.pi/skills/jalco-ui-registry/references/docs-patterns.md b/.pi/skills/jalco-ui-registry/references/docs-patterns.md new file mode 100644 index 0000000..f14646e --- /dev/null +++ b/.pi/skills/jalco-ui-registry/references/docs-patterns.md @@ -0,0 +1,105 @@ +# Docs Patterns + +Use this file when creating or revising Jalco UI docs pages. + +## Canonical spec + +The canonical docs format guide lives at: +- `/Users/justin/Documents/Github/jalco-ui/docs-component-format-spec.md` + +Read that file when making docs structure decisions. + +## Primary goal + +A Jalco UI docs page should help someone: +- understand what the component is +- install it quickly +- see what it looks like +- copy a working usage example +- understand meaningful customization points + +## Required framework + +Public component docs should follow the shared page framework: +1. Metadata +2. Header +3. Preview +4. Installation +5. Usage + +After that, only include justified optional sections such as: +- Requirements +- When to use +- Examples +- Features +- API Reference +- Customization +- Notes + +## Key structural rules + +- Use the standard section order from `docs-component-format-spec.md`. +- Keep docs consistent across the site without forcing every page to have identical sections. +- Treat setup blockers as Requirements or Usage guidance, not buried Notes. +- Do not call every example a variant. +- Keep examples labeled by meaning: Variants, Sizes, Examples, Configurations, or bundled exports. + +## Copy rules + +Follow repo guidance from `AGENTS.md` and the docs format spec: +- start descriptions with a concise sentence +- do not start with "A", "An", or "A React component for..." +- avoid implementation-detail-first descriptions +- avoid fluff and subjective marketing copy in technical summaries +- keep docs description aligned with `registry.json` + +## Installation and requirements + +For public items, include the shadcn CLI command using the Jalco registry URL pattern: + +```bash +npx shadcn@latest add https://ui.justinlevine.me/r/.json +``` + +If a secondary exported component is bundled inside another item, say so clearly instead of pretending it has its own payload. + +If supporting setup materially affects adoption, explain it before Examples. + +## Usage and examples + +Usage should usually include: +- import example +- simplest meaningful render example +- an important runtime note if relevant + +Examples should be: +- realistic +- labeled clearly +- grouped by meaning rather than by arbitrary prop existence + +## API and notes + +Use an API section when the public props are meaningful enough to deserve a formal reference. + +Use Notes for secondary behavior details like cache behavior, fallback behavior, or implementation nuance. +Do not use Notes as a dumping ground for setup blockers or primary usage constraints. + +## Alignment rules + +Ensure these agree: +- page metadata description +- header description +- `registry.json` description +- actual implementation behavior + +If those drift, fix them together. + +## Good docs heuristics + +A good Jalco UI docs page is: +- skimmable +- honest about constraints +- visually polished +- technically precise +- built from realistic examples +- minimal without feeling incomplete diff --git a/.pi/skills/jalco-ui-registry/references/registry-patterns.md b/.pi/skills/jalco-ui-registry/references/registry-patterns.md new file mode 100644 index 0000000..4aa5339 --- /dev/null +++ b/.pi/skills/jalco-ui-registry/references/registry-patterns.md @@ -0,0 +1,117 @@ +# Registry Patterns + +Use this file when editing Jalco UI registry metadata. + +## Main file + +Primary source: +- `/Users/justin/Documents/Github/jalco-ui/registry.json` + +Public mirror currently exists at: +- `/Users/justin/Documents/Github/jalco-ui/public/r/registry.json` + +Treat metadata accuracy as part of the feature. + +## Required alignment + +For each item, align: +- `name` +- folder name under `registry/` +- install URL payload name +- docs page slug +- exported component naming +- title and description + +## Common fields + +### `name` +Lowercase kebab-case item identifier. + +### `type` +Use the correct registry file type: +- `registry:component` +- `registry:block` +- `registry:page` +- `registry:lib` +- `registry:hook` + +Choose the smallest correct type; do not mislabel files for convenience. + +### `title` +Human-facing name shown in docs or discovery contexts. + +### `description` +Short, polished summary aligned with docs copy. + +Good descriptions are: +- concise +- capability-first +- easy to scan +- free of implementation noise unless that behavior is core + +### `dependencies` +Use for npm package dependencies the installed artifact truly requires. + +Examples already in repo: +- `zod` +- `shiki` +- `lucide-react` + +Do not add dependencies that are already covered by registry dependencies or are not actually required by the shipped item. + +### `registryDependencies` +Use for upstream shadcn registry items the installation depends on. + +Examples already in repo: +- `button` +- `card` +- `input` +- `label` +- `textarea` + +### `categories` +Use categories when they improve browsing and organization. +Keep them meaningful and sparse. + +### `files` +Each file must include: +- accurate `path` +- accurate `type` +- `target` only when needed + +## Multi-file item pattern + +A richer item may include: +- main component files +- `lib/*.ts` +- `hooks/*.ts` +- subcomponents + +See `github-stars-button` and `complex-component` for current patterns. + +## Description quality bar + +Registry descriptions should read like polished docs summaries, not placeholders. + +Better: +- `Compact single-line code snippet with syntax highlighting and an inline copy button.` + +Worse: +- `A component for showing code lines in React apps with copy functionality.` + +## Editing rules + +When updating `registry.json`: +- inspect actual imports and file paths first +- verify types and dependency declarations +- check whether docs page copy needs to change too +- avoid adding metadata fields without a clear reason + +## Readiness checklist + +Before finishing metadata changes, verify: +- every listed file exists +- every type is correct +- dependency lists are justified +- title and description match the implementation +- install URL expectations are clear from the item name diff --git a/.pi/skills/jalco-ui-registry/references/repo-map.md b/.pi/skills/jalco-ui-registry/references/repo-map.md new file mode 100644 index 0000000..64e68bf --- /dev/null +++ b/.pi/skills/jalco-ui-registry/references/repo-map.md @@ -0,0 +1,113 @@ +# Jalco UI Repo Map + +Use this file to orient quickly inside `/Users/justin/Documents/Github/jalco-ui`. + +## Primary files + +- `/Users/justin/Documents/Github/jalco-ui/AGENTS.md` + - Canonical repo standards for implementation, docs, composition, and review. +- `/Users/justin/Documents/Github/jalco-ui/README.md` + - Project intent and public positioning. +- `/Users/justin/Documents/Github/jalco-ui/package.json` + - Runtime stack and available scripts. +- `/Users/justin/Documents/Github/jalco-ui/components.json` + - shadcn/ui project configuration. +- `/Users/justin/Documents/Github/jalco-ui/registry.json` + - Main registry metadata source. +- `/Users/justin/Documents/Github/jalco-ui/public/r/registry.json` + - Public-facing registry payload mirror. + +## Main directories + +### `registry/` +Registry item source files. + +Common patterns: +- `registry//.tsx` +- `registry//lib/*.ts` +- `registry//hooks/*.ts` +- `registry/ui/*` for local shadcn-style primitives used by registry items + +Examples: +- `registry/github-stars-button/` +- `registry/waitlist-card/` +- `registry/code-line/` + +### `app/docs/components/` +Docs pages for public components. + +Common pattern: +- `app/docs/components//page.tsx` + +These pages typically include: +- page metadata +- header copy +- preview section +- installation section +- usage section +- examples and/or features +- API reference and notes when justified + +### `components/docs/` +Reusable docs-site building blocks. + +Examples: +- `code-block.tsx` +- `code-block-command.tsx` +- `dependency-badges.tsx` +- `api-ref-table.tsx` +- `sidebar.tsx` + +Use these before inventing new docs-only UI. + +### `prompts/` +Repo-aware prompts that already encode useful Jalco UI workflows. + +Read when useful: +- `prompts/create-jalco-component.md` +- `prompts/review-jalco-component.md` +- `prompts/ideate-jalco-components.md` + +## Best example files + +### Registry implementation examples + +- `/Users/justin/Documents/Github/jalco-ui/registry/github-stars-button/github-stars-button.tsx` + - Strong variant API and server-first implementation. +- `/Users/justin/Documents/Github/jalco-ui/registry/github-stars-button/github-button-group.tsx` + - Good example of a richer API with metrics and segmented interactions. +- `/Users/justin/Documents/Github/jalco-ui/registry/waitlist-card/waitlist-card.tsx` + - Good example of a client component with focused interactivity. +- `/Users/justin/Documents/Github/jalco-ui/registry/code-line/code-line.tsx` + - Good example of server component + client subcomponent split. + +### Docs page examples + +- `/Users/justin/Documents/Github/jalco-ui/app/docs/components/github-stars-button/page.tsx` +- `/Users/justin/Documents/Github/jalco-ui/app/docs/components/github-button-group/page.tsx` +- `/Users/justin/Documents/Github/jalco-ui/app/docs/components/waitlist-card/page.tsx` +- `/Users/justin/Documents/Github/jalco-ui/app/docs/components/code-line/page.tsx` + +Use the closest matching page as the starting point for structure and section selection. + +## Navigation heuristics + +If asked to: +- create a component → inspect `registry/` and the nearest docs page first +- write docs → inspect a matching page under `app/docs/components/` +- change install instructions → inspect docs page + `registry.json` +- change dependencies → inspect both implementation imports and registry metadata +- review registry readiness → inspect implementation, docs page, and `registry.json` together + +## Alignment rules + +Keep these aligned: +- folder name +- item name in `registry.json` +- export name +- docs title +- docs slug/path +- install URL +- descriptive copy + +Misalignment across those surfaces is a Jalco UI quality failure. diff --git a/.pi/skills/jalco-ui-registry/references/review-checklist.md b/.pi/skills/jalco-ui-registry/references/review-checklist.md new file mode 100644 index 0000000..30d348d --- /dev/null +++ b/.pi/skills/jalco-ui-registry/references/review-checklist.md @@ -0,0 +1,114 @@ +# Review Checklist + +Use this file to audit a Jalco UI component, docs page, or registry entry. + +## Verdict categories + +Use one of: +- approved +- approved with changes +- not ready + +## 1. Installability + +Check: +- does the registry item have the right file structure? +- are file paths in `registry.json` correct? +- are dependencies and registryDependencies accurate? +- can a consumer understand how to install it from the docs? + +## 2. API quality + +Check: +- is the public API small and understandable? +- are prop names clear? +- are defaults sensible? +- are variants justified? +- is there boolean-prop sprawl? +- would a consumer know how to adapt it after install? + +## 3. Composition quality + +Check: +- does the component feel shadcn-style and composable? +- does it prefer composition over excessive configuration? +- is `className` supported where appropriate? +- is the implementation readable and easy to own? + +## 4. Accessibility + +Check: +- keyboard interaction +- labels and semantics +- focus-visible treatment +- `aria-live`, `aria-label`, or grouping semantics where relevant +- whether variants preserve accessibility + +## 5. Server/client discipline + +Check: +- is `"use client"` only used when necessary? +- could interactive behavior be isolated into a smaller client subcomponent? +- is server-first behavior documented when important? + +## 6. Styling and design-system consistency + +Check: +- Tailwind utilities and tokens match repo conventions +- variants and sizes feel consistent with shadcn ergonomics +- styling feels polished but not overdesigned +- examples use realistic content + +## 7. Comment and file-header quality + +Check: +- Jalco-authored public-facing source files use Jalco UI's compact header style when appropriate +- public component entry files include the component name, concise description, and key props when helpful +- copied, upstream, generated, or template-derived files are not rewritten just to force comment-style consistency +- dependency or inspiration lines are present only when they add real value +- inline comments are minimal and useful +- there are no decorative AI-style separator banners +- comments do not restate obvious code + +## 8. Docs readiness + +Check: +- concise summary at the top +- installation command is correct +- usage example is realistic +- examples are labeled clearly +- features/API/notes sections only appear when justified +- docs copy matches registry metadata + +## 9. Registry readiness + +Check: +- naming alignment across folder, item name, exports, and docs slug +- correct file types in `registry.json` +- categories and metadata are justified +- descriptions are polished and public-facing + +## 10. Suggested output style for reviews + +Prefer this structure: +1. Verdict +2. What works well +3. Issues found +4. Recommended changes +5. Accessibility notes +6. API and composition notes +7. Styling and design-system notes +8. Comment and file-header notes +9. Registry and docs readiness +10. Suggested commit summary + +## Red flags + +Mark as `not ready` when you see issues like: +- broken naming alignment +- unnecessary client component conversion +- vague or bloated API +- undocumented exposed variants +- placeholder docs copy +- inaccurate registry metadata +- component behavior that is hard to own after installation diff --git a/.pi/skills/jalco-ui-registry/references/variants-and-api.md b/.pi/skills/jalco-ui-registry/references/variants-and-api.md new file mode 100644 index 0000000..deadbb7 --- /dev/null +++ b/.pi/skills/jalco-ui-registry/references/variants-and-api.md @@ -0,0 +1,114 @@ +# Variants and API Design + +Use this file when deciding how a Jalco UI component should expose customization. + +## Core principle + +Jalco UI should feel shadcn-style: +- composable +- easy to install +- easy to read +- easy to adapt +- not overloaded with speculative props + +## When variants are important + +Variants matter when they represent real reusable differences in presentation or interaction, such as: +- visual emphasis levels +- size scales +- shape or density changes used across examples +- segmented states that users will reasonably want to choose between + +Good examples in the repo: +- `github-stars-button` exposes meaningful `variant` and `size` +- `github-button-group` exposes meaningful `variant`, `size`, and metric configuration + +## When not to add variants + +Do not add variants when: +- only one visual style is actually needed +- the variation is too one-off to deserve public API +- the difference is better handled by composition or `className` +- a boolean prop would create hidden style branches and unclear combinations + +## Prefer composition over prop sprawl + +Prefer: +- `children` +- clear slots or subcomponents +- composable wrappers +- `className` escape hatches +- narrow CVA variants with sensible defaults + +Avoid stacking many props like: +- `isCompact` +- `showIcon` +- `withBorder` +- `isMuted` +- `isElevated` +- `showLabel` + +unless those are clearly part of a stable public API. + +## Good public API questions + +Before exposing a prop, ask: +- will most consumers understand what this prop does? +- is it likely to be used more than once? +- does it map to a meaningful design-system choice? +- can it be documented clearly in one sentence? +- does it create hard-to-reason-about prop combinations? + +If the answer is weak, keep the API smaller. + +## Variant documentation rules + +If a component exposes variants publicly: +- show them in the docs +- label the examples clearly +- mention defaults in the API reference when useful +- keep names conventional and unsurprising + +Use labels like: +- Default +- Outline +- Ghost +- Subtle +- Small / Default / Large + +## shadcn-style ergonomics + +Good Jalco UI APIs usually have: +- a sensible default visual style +- a small variant surface +- consistent naming +- `className` support +- type-safe variant unions +- accessibility preserved regardless of variant + +## Server vs client API decisions + +Do not convert a component to client just to support avoidable convenience props. + +Prefer: +- server components by default +- client-only subcomponents for narrowly interactive behavior + +Example pattern: +- `code-line` stays server-rendered +- `code-line-copy-button` is client-only + +## Review checklist for variants + +Approve variants when all are true: +- they reflect real reusable states +- they improve adoption or customization +- they do not bloat the component API +- they are straightforward to preview and document +- they maintain consistent accessibility and styling behavior + +Reject or simplify variants when: +- they feel speculative +- they duplicate what `className` already solves +- they create too many combinations to explain +- they distract from the component's main purpose diff --git a/apps/docs/content/docs/components/activity-graph.mdx b/apps/docs/content/docs/components/activity-graph.mdx index 971047e..4f29ddf 100644 --- a/apps/docs/content/docs/components/activity-graph.mdx +++ b/apps/docs/content/docs/components/activity-graph.mdx @@ -131,8 +131,8 @@ export const sampleData = [ ## Notes -- **Client component.** Uses "use client" for a ResizeObserver that auto-sizes blocks to fit the container. Hover tooltips use native title attributes. +- **Client component.** Uses "use client" for a ResizeObserver that auto-sizes blocks to fit the container. Hover tooltips use Radix Tooltip for styled popover-style date and count display. - **Auto-fit.** By default the block size is computed from the container width so the graph always fits without scrolling. Pass a fixed blockSize to opt into horizontal scrolling instead. -- **ISR caching.** GitHub contribution data is cached for 1 hour via ISR. +- **ISR caching.** The fetch helper uses the [github-contributions-api](https://github.com/grubersjoe/github-contributions-api) which caches upstream for 1 hour, plus ISR caching locally. - **Intensity mapping.** Counts are mapped to four non-zero levels relative to the maximum count in the data. The thresholds are 25%, 50%, 75%, and 100% of the max. -- **No dependencies.** This component uses only React, Tailwind, and the `cn` utility. No charting libraries or external packages. +- **Dependencies.** `radix-ui` for styled hover tooltips on each cell. No charting libraries or external packages. diff --git a/apps/docs/registry.json b/apps/docs/registry.json index 82aca1e..dad6d97 100644 --- a/apps/docs/registry.json +++ b/apps/docs/registry.json @@ -207,6 +207,9 @@ "categories": [ "github" ], + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/activity-graph/activity-graph.tsx", diff --git a/apps/docs/registry/activity-graph/activity-graph.tsx b/apps/docs/registry/activity-graph/activity-graph.tsx index f04e6d6..e8a490f 100644 --- a/apps/docs/registry/activity-graph/activity-graph.tsx +++ b/apps/docs/registry/activity-graph/activity-graph.tsx @@ -18,10 +18,12 @@ * - weeks?: number of weeks to display (default 52) * - className?: additional CSS classes * + * Dependencies: radix-ui (Tooltip) * Inspiration: GitHub contribution graph */ import * as React from "react" +import { Tooltip } from "radix-ui" import { cn } from "@/lib/utils" export interface ActivityEntry { @@ -262,19 +264,38 @@ export function ActivityGraph({ {week.map((day, di) => { const intensity = getIntensity(day.count, maxCount) return ( -
+ + + +
+ + + +

+ {day.count} contribution{day.count === 1 ? "" : "s"} +

+

+ {formatDate(day.date)} +

+ +
+
+ + ) })}
diff --git a/apps/docs/registry/activity-graph/lib/github.ts b/apps/docs/registry/activity-graph/lib/github.ts index f976530..04e47c5 100644 --- a/apps/docs/registry/activity-graph/lib/github.ts +++ b/apps/docs/registry/activity-graph/lib/github.ts @@ -4,8 +4,10 @@ * by Justin Levine * ui.justinlevine.me * - * Fetches GitHub contribution data from the public contributions page. + * Fetches GitHub contribution data via the github-contributions-api. * No API key or authentication required. + * + * @see https://github.com/grubersjoe/github-contributions-api */ import type { ActivityEntry } from "@/registry/activity-graph/activity-graph" @@ -17,26 +19,37 @@ export interface GitHubContributions { entries: ActivityEntry[] } +interface ApiContribution { + date: string + count: number + level: 0 | 1 | 2 | 3 | 4 +} + +interface ApiResponse { + total: Record + contributions: ApiContribution[] +} + /** * Fetch contribution data for a GitHub user. * - * Scrapes the public `github.com/users/{username}/contributions` page, - * which returns an HTML fragment with `data-date`, `data-level`, and - * tooltip text containing exact counts. + * Uses the github-contributions-api which returns structured JSON + * with per-day counts and intensity levels. * * - No GitHub API key required. - * - Caches for 1 hour via Next.js ISR (`next.revalidate`). - * - Returns `null` if the request fails or parsing produces no data. + * - API results are cached upstream for 1 hour. + * - Caches locally for 1 hour via Next.js ISR (`next.revalidate`). + * - Returns `null` if the request fails or produces no data. */ export async function fetchGitHubContributions( username: string ): Promise { try { const response = await fetch( - `https://github.com/users/${encodeURIComponent(username)}/contributions`, + `https://github-contributions-api.jogruber.de/v4/${encodeURIComponent(username)}?y=last`, { headers: { - Accept: "text/html", + Accept: "application/json", "User-Agent": "jalco-ui/1.0", }, next: { revalidate: 3600 }, @@ -45,49 +58,18 @@ export async function fetchGitHubContributions( if (!response.ok) return null - const html = await response.text() - return parseContributionsHtml(html) - } catch { - return null - } -} + const data: ApiResponse = await response.json() -const TOOLTIP_PATTERN = - /for="contribution-day-component-(\d+-\d+)"[^>]*>([^<]+) c.count > 0) + .map((c) => ({ date: c.date, count: c.count })) -const CELL_PATTERN = - /data-date="(\d{4}-\d{2}-\d{2})"[^>]*id="contribution-day-component-(\d+-\d+)"/g + const total = Object.values(data.total).reduce((sum, n) => sum + n, 0) -function parseContributionsHtml( - html: string -): GitHubContributions | null { - const tooltipCounts = new Map() - let match: RegExpExecArray | null + if (entries.length === 0 && total === 0) return null - while ((match = TOOLTIP_PATTERN.exec(html)) !== null) { - const id = match[1] - const text = match[2].trim() - const countMatch = text.match(/^(\d+)\s+contribution/) - tooltipCounts.set(id, countMatch ? parseInt(countMatch[1], 10) : 0) - } - - const entries: ActivityEntry[] = [] - let total = 0 - - while ((match = CELL_PATTERN.exec(html)) !== null) { - const date = match[1] - const id = match[2] - const count = tooltipCounts.get(id) ?? 0 - if (count > 0) { - entries.push({ date, count }) - } - total += count - } - - if (entries.length === 0 && total === 0) { - const hasCells = html.includes("data-date=") - if (!hasCells) return null + return { total, entries } + } catch { + return null } - - return { total, entries } } From 6661f08ff909ff05c19e78f538871fa93886075d Mon Sep 17 00:00:00 2001 From: Justin Levine <20596508+justinlevinedotme@users.noreply.github.com> Date: Wed, 22 Apr 2026 14:21:00 -0700 Subject: [PATCH 2/4] fix(registry): add API attribution, fix sparse docs examples - Add proper credit for github-contributions-api by @grubersjoe in both the lib/github.ts header and the component header - Add attribution note to docs page linking to the API repo - Replace sparse 6-entry sample data with seeded generator producing ~84 days of realistic fill so the blue scale example isn't empty - Main demo now fetches real data via fetchGitHubContributions - Remove weeks={12} from blue scale example so it fills the container --- .../docs/components/activity-graph.mdx | 37 ++++++++++++++----- .../activity-graph/activity-graph.tsx | 1 + .../examples/activity-graph-simple.tsx | 21 ++--------- .../registry/activity-graph/lib/github.ts | 6 +-- 4 files changed, 36 insertions(+), 29 deletions(-) diff --git a/apps/docs/content/docs/components/activity-graph.mdx b/apps/docs/content/docs/components/activity-graph.mdx index 4f29ddf..d8597ff 100644 --- a/apps/docs/content/docs/components/activity-graph.mdx +++ b/apps/docs/content/docs/components/activity-graph.mdx @@ -6,14 +6,32 @@ description: "GitHub-style activity heatmap that visualizes daily counts as a co import { VariantGrid } from "@/components/docs/variant-grid" import { ActivityGraph } from "@/registry/activity-graph/activity-graph" -export const sampleData = [ - { date: "2026-03-01", count: 2 }, - { date: "2026-03-02", count: 5 }, - { date: "2026-03-03", count: 12 }, - { date: "2026-03-04", count: 0 }, - { date: "2026-03-05", count: 8 }, - { date: "2026-03-06", count: 4 }, -] +export function seededRandom(seed) { + let s = seed + return () => { + s = (s * 16807 + 0) % 2147483647 + return s / 2147483647 + } +} + +export function generateSampleData(days = 84, seed = 42) { + const rand = seededRandom(seed) + const entries = [] + const today = new Date("2026-04-22") + for (let i = days; i >= 0; i--) { + const d = new Date(today) + d.setDate(today.getDate() - i) + if (rand() < 0.55) { + entries.push({ + date: d.toISOString().slice(0, 10), + count: Math.floor(rand() * 12) + 1, + }) + } + } + return entries +} + +export const sampleData = generateSampleData() @@ -41,7 +59,7 @@ export const sampleData = [ { label: "Blue scale", code: ``, - preview: , + preview: , }, ]} /> @@ -136,3 +154,4 @@ export const sampleData = [ - **ISR caching.** The fetch helper uses the [github-contributions-api](https://github.com/grubersjoe/github-contributions-api) which caches upstream for 1 hour, plus ISR caching locally. - **Intensity mapping.** Counts are mapped to four non-zero levels relative to the maximum count in the data. The thresholds are 25%, 50%, 75%, and 100% of the max. - **Dependencies.** `radix-ui` for styled hover tooltips on each cell. No charting libraries or external packages. +- **Attribution.** The fetch helper uses the [github-contributions-api](https://github.com/grubersjoe/github-contributions-api) by [@grubersjoe](https://github.com/grubersjoe). diff --git a/apps/docs/registry/activity-graph/activity-graph.tsx b/apps/docs/registry/activity-graph/activity-graph.tsx index e8a490f..b4240bb 100644 --- a/apps/docs/registry/activity-graph/activity-graph.tsx +++ b/apps/docs/registry/activity-graph/activity-graph.tsx @@ -20,6 +20,7 @@ * * Dependencies: radix-ui (Tooltip) * Inspiration: GitHub contribution graph + * Data fetching powered by github-contributions-api by @grubersjoe */ import * as React from "react" diff --git a/apps/docs/registry/activity-graph/examples/activity-graph-simple.tsx b/apps/docs/registry/activity-graph/examples/activity-graph-simple.tsx index 95a9746..04ec33a 100644 --- a/apps/docs/registry/activity-graph/examples/activity-graph-simple.tsx +++ b/apps/docs/registry/activity-graph/examples/activity-graph-simple.tsx @@ -1,23 +1,10 @@ import { ActivityGraph } from "@/registry/activity-graph/activity-graph" +import { fetchGitHubContributions } from "@/registry/activity-graph/lib/github" export default async function ActivityGraphDemo() { - const contributions = [ - { date: "2026-01-01", count: 2 }, - { date: "2026-01-02", count: 5 }, - { date: "2026-01-03", count: 12 }, - { date: "2026-01-04", count: 0 }, - { date: "2026-01-05", count: 8 }, - { date: "2026-01-06", count: 4 }, - ] - + const contributions = await fetchGitHubContributions("jlevine22") + return ( -
-
-

- Note: This example uses static data. Use the fetch helper for real GitHub data. -

- -
-
+ ) } diff --git a/apps/docs/registry/activity-graph/lib/github.ts b/apps/docs/registry/activity-graph/lib/github.ts index 04e47c5..9c6bd60 100644 --- a/apps/docs/registry/activity-graph/lib/github.ts +++ b/apps/docs/registry/activity-graph/lib/github.ts @@ -4,10 +4,10 @@ * by Justin Levine * ui.justinlevine.me * - * Fetches GitHub contribution data via the github-contributions-api. - * No API key or authentication required. + * Fetches GitHub contribution data via the github-contributions-api + * by Jonathan Gruber (@grubersjoe). No API key or authentication required. * - * @see https://github.com/grubersjoe/github-contributions-api + * Attribution: https://github.com/grubersjoe/github-contributions-api */ import type { ActivityEntry } from "@/registry/activity-graph/activity-graph" From 455ae4450fb2c958007598c744fdc53f9c4058df Mon Sep 17 00:00:00 2001 From: Justin Levine <20596508+justinlevinedotme@users.noreply.github.com> Date: Wed, 22 Apr 2026 14:21:51 -0700 Subject: [PATCH 3/4] fix(registry): dedupe month labels that span year boundary Track year+month instead of just month index in getMonthLabels so Apr 2025 and Apr 2026 don't both render as duplicate 'Apr' labels. --- apps/docs/registry/activity-graph/activity-graph.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/docs/registry/activity-graph/activity-graph.tsx b/apps/docs/registry/activity-graph/activity-graph.tsx index b4240bb..ccdc88d 100644 --- a/apps/docs/registry/activity-graph/activity-graph.tsx +++ b/apps/docs/registry/activity-graph/activity-graph.tsx @@ -132,18 +132,18 @@ function getMonthLabels( blockSize: number ): { label: string; offset: number }[] { const months: { label: string; offset: number }[] = [] - let lastMonth = -1 + let lastKey = "" for (let w = 0; w < weeks.length; w++) { const firstDay = weeks[w][0] - const month = firstDay.date.getMonth() + const key = `${firstDay.date.getFullYear()}-${firstDay.date.getMonth()}` - if (month !== lastMonth) { + if (key !== lastKey) { months.push({ label: firstDay.date.toLocaleString("en-US", { month: "short" }), offset: w * (blockSize + GAP), }) - lastMonth = month + lastKey = key } } From 7223a983ab9cd734b0d36708d6e2f8aef7c3c38a Mon Sep 17 00:00:00 2001 From: Justin Levine <20596508+justinlevinedotme@users.noreply.github.com> Date: Wed, 22 Apr 2026 14:24:30 -0700 Subject: [PATCH 4/4] fix(registry): use local logo assets in logo-cloud demos Replace external svgl.app URLs with local /logos/*.svg files so logos load instantly instead of popping in sporadically from a third-party CDN. --- apps/docs/app/sponsor/page.tsx | 44 +++++++++---------- .../logo-cloud/examples/logo-cloud-demo.tsx | 16 +++---- .../logo-cloud/examples/logo-cloud-static.tsx | 12 ++--- 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/apps/docs/app/sponsor/page.tsx b/apps/docs/app/sponsor/page.tsx index 62a14b3..dcbcfe3 100644 --- a/apps/docs/app/sponsor/page.tsx +++ b/apps/docs/app/sponsor/page.tsx @@ -22,30 +22,28 @@ interface Stargazer { } async function getStargazers(): Promise { - try { - const pages: Stargazer[] = [] - let page = 1 - // Fetch up to 5 pages (500 stargazers) to be safe - while (page <= 5) { - const res = await fetch( - `https://api.github.com/repos/jal-co/ui/stargazers?per_page=100&page=${page}`, - { - headers: { Accept: "application/vnd.github.v3+json" }, - next: { revalidate: 3600 }, - } - ) - if (!res.ok) break - const data: Stargazer[] = await res.json() - if (data.length === 0) break - pages.push(...data) - if (data.length < 100) break - page++ - } - // Filter out the org account - return pages.filter((s) => s.login !== "jal-co") - } catch { - return [] + const pages: Stargazer[] = [] + let page = 1 + // Fetch up to 5 pages (500 stargazers) to be safe + while (page <= 5) { + const res = await fetch( + `https://api.github.com/repos/jal-co/ui/stargazers?per_page=100&page=${page}`, + { + headers: { Accept: "application/vnd.github.v3+json" }, + // Revalidate every hour, but keep serving stale data on error + // so the section never disappears from a transient failure. + next: { revalidate: 3600, tags: ["stargazers"] }, + } + ) + if (!res.ok) break + const data: Stargazer[] = await res.json() + if (data.length === 0) break + pages.push(...data) + if (data.length < 100) break + page++ } + // Filter out the org account + return pages.filter((s) => s.login !== "jal-co") } const tiers = [ diff --git a/apps/docs/registry/logo-cloud/examples/logo-cloud-demo.tsx b/apps/docs/registry/logo-cloud/examples/logo-cloud-demo.tsx index 1e7d040..8d23d35 100644 --- a/apps/docs/registry/logo-cloud/examples/logo-cloud-demo.tsx +++ b/apps/docs/registry/logo-cloud/examples/logo-cloud-demo.tsx @@ -1,14 +1,14 @@ import { LogoCloudMarquee } from "@/registry/logo-cloud/logo-cloud" const logos = [ - { src: "https://svgl.app/library/nextjs_icon_dark.svg", alt: "Next.js" }, - { src: "https://svgl.app/library/react_dark.svg", alt: "React" }, - { src: "https://svgl.app/library/tailwindcss.svg", alt: "Tailwind CSS" }, - { src: "https://svgl.app/library/typescript.svg", alt: "TypeScript" }, - { src: "https://svgl.app/library/vercel_dark.svg", alt: "Vercel" }, - { src: "https://svgl.app/library/github_dark.svg", alt: "GitHub" }, - { src: "https://svgl.app/library/linear.svg", alt: "Linear" }, - { src: "https://svgl.app/library/stripe.svg", alt: "Stripe" }, + { src: "/logos/nextjs.svg", alt: "Next.js" }, + { src: "/logos/react.svg", alt: "React" }, + { src: "/logos/tailwindcss.svg", alt: "Tailwind CSS" }, + { src: "/logos/typescript.svg", alt: "TypeScript" }, + { src: "/logos/vercel.svg", alt: "Vercel" }, + { src: "/logos/github.svg", alt: "GitHub" }, + { src: "/logos/linear.svg", alt: "Linear" }, + { src: "/logos/stripe.svg", alt: "Stripe" }, ] export default function LogoCloudDemo() { diff --git a/apps/docs/registry/logo-cloud/examples/logo-cloud-static.tsx b/apps/docs/registry/logo-cloud/examples/logo-cloud-static.tsx index 0e8de7c..39cd3ad 100644 --- a/apps/docs/registry/logo-cloud/examples/logo-cloud-static.tsx +++ b/apps/docs/registry/logo-cloud/examples/logo-cloud-static.tsx @@ -1,12 +1,12 @@ import { LogoCloud } from "@/registry/logo-cloud/logo-cloud" const logos = [ - { src: "https://svgl.app/library/nextjs_icon_dark.svg", alt: "Next.js" }, - { src: "https://svgl.app/library/react_dark.svg", alt: "React" }, - { src: "https://svgl.app/library/tailwindcss.svg", alt: "Tailwind CSS" }, - { src: "https://svgl.app/library/typescript.svg", alt: "TypeScript" }, - { src: "https://svgl.app/library/vercel_dark.svg", alt: "Vercel" }, - { src: "https://svgl.app/library/github_dark.svg", alt: "GitHub" }, + { src: "/logos/nextjs.svg", alt: "Next.js" }, + { src: "/logos/react.svg", alt: "React" }, + { src: "/logos/tailwindcss.svg", alt: "Tailwind CSS" }, + { src: "/logos/typescript.svg", alt: "TypeScript" }, + { src: "/logos/vercel.svg", alt: "Vercel" }, + { src: "/logos/github.svg", alt: "GitHub" }, ] export default function LogoCloudStatic() {