diff --git a/README.md b/README.md index beb307f..296356d 100644 --- a/README.md +++ b/README.md @@ -14,22 +14,22 @@

- npm version - npm downloads per month - total npm downloads - minzipped size - GitHub stars - Sponsor - license + npm version + npm downloads per month + total npm downloads + minzipped size + GitHub stars + Sponsor + license

- zero dependencies - TypeScript - SSR-safe - tree-shakable - ESM + CJS - 40 hooks + zero dependencies + TypeScript + SSR-safe + tree-shakable + ESM + CJS + 40 hooks

diff --git a/apps/docs/AGENTS.md b/apps/docs/AGENTS.md index 983abd3..636bfcc 100644 --- a/apps/docs/AGENTS.md +++ b/apps/docs/AGENTS.md @@ -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. @@ -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 diff --git a/apps/docs/README.md b/apps/docs/README.md index e12d8a9..3a00994 100644 --- a/apps/docs/README.md +++ b/apps/docs/README.md @@ -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. diff --git a/apps/docs/app/apple-icon.tsx b/apps/docs/app/apple-icon.tsx index 7f79ca4..eb83934 100644 --- a/apps/docs/app/apple-icon.tsx +++ b/apps/docs/app/apple-icon.tsx @@ -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"; @@ -17,7 +19,7 @@ export default function AppleIcon() { display: "flex", alignItems: "center", justifyContent: "center", - backgroundColor: OG_COLORS.ground, + backgroundColor: "#151c27", }} > diff --git a/apps/docs/app/docs/[slug]/opengraph-image.tsx b/apps/docs/app/docs/[slug]/opengraph-image.tsx index e357035..8e77cb3 100644 --- a/apps/docs/app/docs/[slug]/opengraph-image.tsx +++ b/apps/docs/app/docs/[slug]/opengraph-image.tsx @@ -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 diff --git a/apps/docs/app/docs/page.tsx b/apps/docs/app/docs/page.tsx index f0c3c26..16b5458 100644 --- a/apps/docs/app/docs/page.tsx +++ b/apps/docs/app/docs/page.tsx @@ -49,21 +49,21 @@ export default function DocsIndexPage() {

Quick start

  1. - 1 + 1

    Install

    Add hookli to your React app.

  2. - 2 + 2

    Import

    Pull in exactly the hook you need.

    - + import {"{ useToggle }"} from "hookli"
  3. - 3 + 3

    Use it

    Call it in a component — no providers, no config.

  4. @@ -82,7 +82,7 @@ export default function DocsIndexPage() {

    Hooks

    {CATEGORY_ORDER.map((category) => (
    -

    +

    {CATEGORY_LABELS[category]}

    diff --git a/apps/docs/app/globals.css b/apps/docs/app/globals.css index cf95454..906904a 100644 --- a/apps/docs/app/globals.css +++ b/apps/docs/app/globals.css @@ -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 . */ @@ -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); } @@ -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); } @@ -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); } @@ -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); } @@ -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; } diff --git a/apps/docs/app/layout.tsx b/apps/docs/app/layout.tsx index 7379cef..797a71d 100644 --- a/apps/docs/app/layout.tsx +++ b/apps/docs/app/layout.tsx @@ -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", }; diff --git a/apps/docs/app/opengraph-image.tsx b/apps/docs/app/opengraph-image.tsx index 74bde91..8b39dbd 100644 --- a/apps/docs/app/opengraph-image.tsx +++ b/apps/docs/app/opengraph-image.tsx @@ -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 diff --git a/apps/docs/app/page.tsx b/apps/docs/app/page.tsx index 7b0e4b6..fdcb166 100644 --- a/apps/docs/app/page.tsx +++ b/apps/docs/app/page.tsx @@ -20,11 +20,11 @@ const HERO_FACTS = [ export default function Home() { return (
    -
    +