Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,22 @@
</p>

<p align="center">
<a href="https://www.npmjs.com/package/hookli"><img src="https://img.shields.io/npm/v/hookli?style=flat-square&color=003748" alt="npm version" /></a>
<a href="https://www.npmjs.com/package/hookli"><img src="https://img.shields.io/npm/dm/hookli?style=flat-square&color=30C5CA&label=downloads%2Fmonth" alt="npm downloads per month" /></a>
<a href="https://www.npmjs.com/package/hookli"><img src="https://img.shields.io/npm/dt/hookli?style=flat-square&color=003748&label=total%20downloads" alt="total npm downloads" /></a>
<a href="https://bundlephobia.com/package/hookli"><img src="https://img.shields.io/bundlephobia/minzip/hookli?style=flat-square&color=30C5CA" alt="minzipped size" /></a>
<a href="https://github.com/saifmohamedsv/hookli"><img src="https://img.shields.io/github/stars/saifmohamedsv/hookli?style=flat-square&color=003748" alt="GitHub stars" /></a>
<a href="https://github.com/sponsors/saifmohamedsv"><img src="https://img.shields.io/badge/sponsor-30C5CA?style=flat-square&logo=githubsponsors&logoColor=white" alt="Sponsor" /></a>
<img src="https://img.shields.io/npm/l/hookli?style=flat-square&color=8FB6C2" alt="license" />
<a href="https://www.npmjs.com/package/hookli"><img src="https://img.shields.io/npm/v/hookli?style=flat-square&color=0A0D12" alt="npm version" /></a>
<a href="https://www.npmjs.com/package/hookli"><img src="https://img.shields.io/npm/dm/hookli?style=flat-square&color=61DAFB&label=downloads%2Fmonth" alt="npm downloads per month" /></a>
<a href="https://www.npmjs.com/package/hookli"><img src="https://img.shields.io/npm/dt/hookli?style=flat-square&color=0A0D12&label=total%20downloads" alt="total npm downloads" /></a>
<a href="https://bundlephobia.com/package/hookli"><img src="https://img.shields.io/bundlephobia/minzip/hookli?style=flat-square&color=61DAFB" alt="minzipped size" /></a>
<a href="https://github.com/saifmohamedsv/hookli"><img src="https://img.shields.io/github/stars/saifmohamedsv/hookli?style=flat-square&color=0A0D12" alt="GitHub stars" /></a>
<a href="https://github.com/sponsors/saifmohamedsv"><img src="https://img.shields.io/badge/sponsor-61DAFB?style=flat-square&logo=githubsponsors&logoColor=0A0D12" alt="Sponsor" /></a>
<img src="https://img.shields.io/npm/l/hookli?style=flat-square&color=354355" alt="license" />
</p>

<p align="center">
<img src="https://img.shields.io/badge/zero_dependencies-30C5CA?style=for-the-badge" alt="zero dependencies" />
<img src="https://img.shields.io/badge/TypeScript-003748?style=for-the-badge" alt="TypeScript" />
<img src="https://img.shields.io/badge/SSR--safe-30C5CA?style=for-the-badge" alt="SSR-safe" />
<img src="https://img.shields.io/badge/tree--shakable-003748?style=for-the-badge" alt="tree-shakable" />
<img src="https://img.shields.io/badge/ESM_%2B_CJS-30C5CA?style=for-the-badge" alt="ESM + CJS" />
<img src="https://img.shields.io/badge/40_hooks-003748?style=for-the-badge" alt="40 hooks" />
<img src="https://img.shields.io/badge/zero_dependencies-61DAFB?style=for-the-badge" alt="zero dependencies" />
<img src="https://img.shields.io/badge/TypeScript-0A0D12?style=for-the-badge" alt="TypeScript" />
<img src="https://img.shields.io/badge/SSR--safe-61DAFB?style=for-the-badge" alt="SSR-safe" />
<img src="https://img.shields.io/badge/tree--shakable-0A0D12?style=for-the-badge" alt="tree-shakable" />
<img src="https://img.shields.io/badge/ESM_%2B_CJS-61DAFB?style=for-the-badge" alt="ESM + CJS" />
<img src="https://img.shields.io/badge/40_hooks-0A0D12?style=for-the-badge" alt="40 hooks" />
</p>

<p align="center">
Expand Down
29 changes: 21 additions & 8 deletions apps/docs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,29 @@ repo (`../rehooks-app`), which is **read-only context** — never modify it from
Next.js (App Router, TypeScript, Tailwind, npm). Hooks for live demos are imported from
the **published `hookli` package** (in node_modules), not from relative paths.

## Brand (v3 — dark-first, solid, no gradients)
- The base is a **near-black neutral** (faint teal undertone), NOT a saturated fill. Surfaces are
defined by subtle elevation + hairline borders, and the brand hues are **accents, not backgrounds**:
ground `#0A1315` (page) → raised `#101D21` (cards) → overlay `#17282E` (hover/nested); hairline
borders via `border-slate-syntax/40`. Accent `#30C5CA` (Scooter) = links/focus/CTA/active; Prussian
`#003748` (`--color-brand`) = select brand moments only; ink `#FFFFFF`, muted `#94B0B8`.
## Brand (Premium Dark v1.0 — cool near-black ramp, dark-first, solid, no gradients)
- A cool near-black refinement of the React palette. The base is a near-black neutral ramp, NOT a
saturated fill, and the brand hues are **accents, not backgrounds**: ground `#0A0D12` (page +
inset wells) → raised `#10151D` (cards, ~6% lift) → overlay `#151C27` (hover/nested/elevated
**surface** wash). Insets (code interiors, in-card inputs) cut back to `bg-ground`.
**Elevation comes from an edge, not a fill** — adjacent surfaces are only ~6% apart, so depth is
a solid frame border + a 1px top highlight + an ambient shadow (`.surface`/`.surface-lift` in
`globals.css`), never a lighter fill. Borders have **three roles** (solid, never alpha-faded):
the FRAME edge uses `border-slate-syntax` (`#354355` — the visible card/code-window/major-chrome
edge, never text); QUIET row dividers inside a card use `border-divider` (`#242D3A` — near-
invisible; NOTE `ground-overlay` is now a surface fill tone, never a border); interactive
outlines (outline buttons, inputs, toggles) use `border-gray-outline` (`#697586` — ≥3:1 non-text
on every surface, chrome never copy), stepping to `border-gray-body` on hover / `border-accent`
when active. One carve-out: on the light `bg-slate-50` demo panels (dark-mode demos),
`text-slate-syntax` IS the correct copy color (~9:1 there; gray-body would be too faint) — don't
"fix" those call sites. Accent `#61DAFB` (classic React cyan) = links/focus/CTA/active —
**text-grade**, lifting to `#7CE2FF` (`accent-hover`) on hover. Brand `#087EA4` (react.dev teal,
`--color-brand`) = **large decorative fills only** — fails AA for text/small UI. Body/ink
`#FFFFFF` (pure white), muted `#9BA8B8` (gray-body — the copy floor).
- Colors + fonts live as CSS variables in `app/globals.css` (`@theme`). Components reference the
resulting Tailwind tokens (`bg-ground`, `bg-ground-raised`, `bg-ground-overlay`, `text-accent`,
`text-fg`, `text-gray-body`, `border-*`) — **never** a raw hex or arbitrary value.
- Wordmark: `hookli.` (lowercase, teal full stop) beside the hook mark (inline SVG from
- Wordmark: `hookli.` (lowercase, cyan full stop) beside the hook mark (inline SVG from
`public/hookli-icon.svg`). Assets in `public/` (`hookli-banner.svg/.png`, `hookli-icon.svg`).
- **Plus Jakarta Sans** for headings + body; monospace ONLY inside code surfaces (code block,
install command, API value/type cells, demo inputs). Inline SVG icons — never emoji as icons.
Expand Down Expand Up @@ -100,7 +113,7 @@ App Router files keep their framework names (`page.tsx`, `layout.tsx`, `not-foun

Style with Tailwind utilities bound to the `@theme` tokens (`bg-ground`, `bg-ground-raised`, `text-accent`,
`text-fg`, `text-gray-body`, `border-*`, `font-sans`, `font-mono`). **Never** a hardcoded hex, an arbitrary
color (`text-[#30c5ca]`), or an inline `style` color — add a token to `globals.css` first, then reference it.
color (`text-[#61dafb]`), or an inline `style` color — add a token to `globals.css` first, then reference it.

### 7 — Accessibility

Expand Down
4 changes: 2 additions & 2 deletions apps/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ and one demo component:
## Conventions & brand

Engineering conventions (kebab-case files, named exports, token-only styling, import order,
barrels) are documented in [`AGENTS.md`](./AGENTS.md). The visual system (brand v3: dark-first
Prussian/Scooter palette, tonal depth scale, typography) lives in
barrels) are documented in [`AGENTS.md`](./AGENTS.md). The visual system (brand v4: the official
React palette — dark neutral grays + React cyan — tonal depth scale, typography) lives in
[`docs/DESIGN.md`](./docs/DESIGN.md) and is implemented purely through the `@theme` tokens in
`app/globals.css` — components reference tokens (`bg-ground`, `text-accent`), never raw hex.

Expand Down
6 changes: 4 additions & 2 deletions apps/docs/app/apple-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { ImageResponse } from "next/og";
import { OG_COLORS } from "@/lib/og";

/* Apple-touch-icon variant of public/hookli-icon.svg: the hook mark on a
full-bleed ground tile; iOS applies its own corner mask. */
full-bleed tile; iOS applies its own corner mask. The tile uses the Premium
Dark chip tone (#151c27 — ground-overlay), matching the icon SVG's lifted
chip rather than the flatter OG ground. */

export const size = { width: 180, height: 180 };
export const contentType = "image/png";
Expand All @@ -17,7 +19,7 @@ export default function AppleIcon() {
display: "flex",
alignItems: "center",
justifyContent: "center",
backgroundColor: OG_COLORS.ground,
backgroundColor: "#151c27",
}}
>
<svg width={80} height={102} viewBox="-13 -3 110 140" fill="none">
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/app/docs/[slug]/opengraph-image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export default async function Image({
style={{
fontFamily: "JetBrains Mono",
fontSize: 26,
color: OG_COLORS.slate,
color: OG_COLORS.gray,
}}
>
npm i hookli
Expand Down
10 changes: 5 additions & 5 deletions apps/docs/app/docs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,21 @@ export default function DocsIndexPage() {
<h2 className="mt-12 text-2xl">Quick start</h2>
<ol className="mt-4 grid gap-3 sm:grid-cols-3">
<li className="surface flex flex-col rounded-xl p-5">
<span className="flex size-7 items-center justify-center rounded-full bg-accent/10 text-sm font-semibold text-accent ring-1 ring-inset ring-accent/20">1</span>
<span className="flex size-7 items-center justify-center rounded-full bg-accent/10 text-sm font-semibold text-accent ring-1 ring-inset ring-accent">1</span>
<h3 className="mt-4 text-base font-semibold">Install</h3>
<p className="mt-1 text-sm leading-relaxed text-gray-body">Add hookli to your React app.</p>
<InstallCommand className="mt-3" />
</li>
<li className="surface flex flex-col rounded-xl p-5">
<span className="flex size-7 items-center justify-center rounded-full bg-accent/10 text-sm font-semibold text-accent ring-1 ring-inset ring-accent/20">2</span>
<span className="flex size-7 items-center justify-center rounded-full bg-accent/10 text-sm font-semibold text-accent ring-1 ring-inset ring-accent">2</span>
<h3 className="mt-4 text-base font-semibold">Import</h3>
<p className="mt-1 text-sm leading-relaxed text-gray-body">Pull in exactly the hook you need.</p>
<code className="surface mt-3 block overflow-x-auto rounded-lg px-3 py-2 font-mono text-xs">
<code className="mt-3 block overflow-x-auto rounded-lg border border-slate-syntax bg-ground px-3 py-2 font-mono text-xs">
<span className="text-accent">import</span> {"{ useToggle }"} <span className="text-accent">from</span> <span className="text-gray-body">&quot;hookli&quot;</span>
</code>
</li>
<li className="surface flex flex-col rounded-xl p-5">
<span className="flex size-7 items-center justify-center rounded-full bg-accent/10 text-sm font-semibold text-accent ring-1 ring-inset ring-accent/20">3</span>
<span className="flex size-7 items-center justify-center rounded-full bg-accent/10 text-sm font-semibold text-accent ring-1 ring-inset ring-accent">3</span>
<h3 className="mt-4 text-base font-semibold">Use it</h3>
<p className="mt-1 text-sm leading-relaxed text-gray-body">Call it in a component — no providers, no config.</p>
</li>
Expand All @@ -82,7 +82,7 @@ export default function DocsIndexPage() {
<h2 className="mt-12 text-2xl">Hooks</h2>
{CATEGORY_ORDER.map((category) => (
<section key={category} className="mt-8" aria-label={CATEGORY_LABELS[category]}>
<h3 className="text-xs font-semibold uppercase tracking-wider text-slate-syntax">
<h3 className="text-xs font-semibold uppercase tracking-wider text-gray-body">
{CATEGORY_LABELS[category]}
</h3>
<div className="mt-3 grid gap-3 sm:grid-cols-2">
Expand Down
68 changes: 43 additions & 25 deletions apps/docs/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
@import "tailwindcss";

/* Brand v3 tokens — docs/DESIGN.md §1. Solid colors only, dark-first, no light theme.
Tonal depth (T19): #003748 is a brand MID-tone, so it's the SURFACE, not the page.
The page ground is a much darker near-black blue in the same hue family, letting
cards/panels visibly lift; a third step lifts nested/hover surfaces above the card. */
/* Premium Dark v1.0 — a cool near-black premium-dark ramp (refines the React
palette). The page sits at a near-black, slightly cool #0a0d12; cards and
panels lift only ~6% to #10151d, and hover/nested washes rise another step to
#151c27. Adjacent surfaces are deliberately close, so ELEVATION COMES FROM AN
EDGE, NOT A FILL: a solid frame border + a 1px top highlight (light catching
the top edge) + a soft ambient shadow. Three border roles keep the chrome
legible on this dense ramp — FRAME `slate-syntax` (the visible structural
edge on cards, code windows, major chrome), QUIET `divider` (near-invisible
row rules inside dense lists/tables), and INTERACTIVE `gray-outline` (the
only ≥3:1 tier — inputs, outline buttons, toggles). #61DAFB (classic React
cyan) stays the TEXT-GRADE accent, lifting to #7CE2FF on hover; #087EA4
(react.dev brand teal) is large-decorative-only and fails AA for text. */
@theme {
/* Near-black neutral base with a faint teal undertone — surfaces are defined by
subtle elevation + hairline borders, NOT by saturated fills. Prussian (#003748)
and Scooter (#30c5ca) are reserved as ACCENTS, not backgrounds. */
--color-ground: #0a1315; /* page */
--color-ground-raised: #101d21; /* cards / panels — a subtle lift off the page */
--color-ground-overlay: #17282e; /* hover / nested / code-line highlight */
--color-brand: #003748; /* Prussian — for select brand moments only */
--color-accent: #30c5ca; /* Scooter — links, focus, CTA, active state */
--color-slate-syntax: #4a7a8c; /* hairline borders (used at /40) + code comments */
--color-gray-body: #94b0b8; /* secondary text */
--color-fg: #ffffff;
/* Surfaces are defined by elevation + a solid frame edge, NOT by saturated
fills — the React hues stay accents. Tier semantics: ground (page + inset
wells) → raised (cards/panels) → overlay (hover / nested / elevated wash). */
--color-ground: #0a0d12; /* page + inset wells — near-black, cool */
--color-ground-raised: #10151d; /* cards / panels — ~6% lift off the page */
--color-ground-overlay: #151c27; /* hover / nested / elevated wash — the top step */
--color-accent: #61dafb; /* classic React cyan — links, focus, CTA, active (text-grade) */
--color-accent-hover: #7ce2ff; /* accent hover — brighter cyan for CTAs/links on hover */
--color-brand: #087ea4; /* react.dev brand teal — LARGE DECORATIVE use only, never text/small UI */
--color-slate-syntax: #354355; /* FRAME edge — solid 1px card/code-window/major-chrome borders, NOT text */
--color-divider: #242d3a; /* QUIET row divider — near-invisible rules inside dense lists/tables */
--color-gray-outline: #697586; /* INTERACTIVE outline — inputs, outline buttons, toggles (only tier ≥3:1 on every surface) */
--color-gray-body: #9ba8b8; /* muted/secondary text — AA on every surface (min 6.5:1) */
--color-fg: #ffffff; /* primary ink — pure white */
}

/* Referenced at runtime from the next/font variables on <html>. */
Expand All @@ -40,7 +51,9 @@
--shiki-token-string-expression: var(--color-gray-body);
--shiki-token-parameter: var(--color-gray-body);
--shiki-token-punctuation: var(--color-gray-body);
--shiki-token-comment: var(--color-slate-syntax);
/* Comments are text — slate (gray-70) is border-grade on the react.dev ramp
and fails AA on code surfaces, so comments read in the muted text gray. */
--shiki-token-comment: var(--color-gray-body);
--shiki-token-link: var(--color-accent);
}

Expand Down Expand Up @@ -91,15 +104,18 @@ body {

/* ── Elevation system ─────────────────────────────────────────────────────
Near-black-on-near-black means depth can't come from a drop shadow alone —
it comes from a hairline border, a 1px top highlight (light catching the top
edge), and a soft ambient shadow. `.surface` is the static card treatment;
`.surface-lift` adds a 150–200ms ease-out hover raise for interactive cards. */
it comes from a crisp solid 1px border, a 1px top highlight (light catching
the top edge), and a soft ambient shadow. `.surface` is the static card
treatment; `.surface-lift` adds a 150–200ms ease-out hover raise for
interactive cards. */
@layer components {
.surface {
background-color: var(--color-ground-raised);
border: 1px solid color-mix(in srgb, var(--color-slate-syntax) 26%, transparent);
/* Solid gray-70, react.dev-style — every card/frame border runs at full
strength; alpha-faded hairlines vanish on this ramp. */
border: 1px solid var(--color-slate-syntax);
box-shadow:
inset 0 1px 0 0 color-mix(in srgb, var(--color-fg) 6%, transparent),
inset 0 1px 0 0 color-mix(in srgb, var(--color-fg) 8%, transparent),
0 1px 2px 0 color-mix(in srgb, black 40%, transparent),
0 12px 28px -18px color-mix(in srgb, black 70%, transparent);
}
Expand All @@ -115,7 +131,7 @@ body {
transform: translateY(-3px);
border-color: color-mix(in srgb, var(--color-accent) 45%, transparent);
box-shadow:
inset 0 1px 0 0 color-mix(in srgb, var(--color-fg) 9%, transparent),
inset 0 1px 0 0 color-mix(in srgb, var(--color-fg) 11%, transparent),
0 2px 4px 0 color-mix(in srgb, black 40%, transparent),
0 20px 44px -20px color-mix(in srgb, black 85%, transparent);
}
Expand Down Expand Up @@ -171,9 +187,11 @@ body {
animation: copy-pop 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Subtle inner shadow at the top of a code pane, separating it from the
filename caption above (premium code-block chrome). */
/* Code-pane interior: an inset well — it cuts through the raised frame back to
the page ground for terminal depth, plus a subtle inner shadow at the top
separating it from the filename caption above (premium code-block chrome). */
.code-pane {
background-color: var(--color-ground);
box-shadow: inset 0 10px 12px -12px color-mix(in srgb, black 85%, transparent);
}

Expand Down Expand Up @@ -222,7 +240,7 @@ body {
width: 1rem;
margin-right: 1.25rem;
text-align: right;
color: var(--color-slate-syntax);
color: var(--color-gray-body);
user-select: none;
}

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const metadata: Metadata = {
export const viewport: Viewport = {
// Mobile browser chrome — matches --color-ground (globals.css). A theme-color
// meta tag requires a literal hex; keep this in sync with the ground token.
themeColor: "#04191f",
themeColor: "#0a0d12",
colorScheme: "dark",
};

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/app/opengraph-image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default async function Image() {
bottom: 48,
fontFamily: "JetBrains Mono",
fontSize: 26,
color: OG_COLORS.slate,
color: OG_COLORS.gray,
}}
>
npm i hookli
Expand Down
Loading
Loading