img-upload-kit 0.0.0-stage → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -0
- package/LICENSE +21 -0
- package/README.md +258 -2
- package/dist/blur.cjs +413 -0
- package/dist/blur.cjs.map +1 -0
- package/dist/blur.d.cts +17 -0
- package/dist/blur.d.ts +17 -0
- package/dist/blur.js +395 -0
- package/dist/blur.js.map +1 -0
- package/dist/blurService-DqsnfPTd.d.cts +172 -0
- package/dist/blurService-DqsnfPTd.d.ts +172 -0
- package/dist/cli.cjs +1044 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +99 -0
- package/dist/cli.d.ts +99 -0
- package/dist/cli.js +998 -0
- package/dist/cli.js.map +1 -0
- package/dist/headless.cjs +1899 -0
- package/dist/headless.cjs.map +1 -0
- package/dist/headless.d.cts +2 -0
- package/dist/headless.d.ts +2 -0
- package/dist/headless.js +1888 -0
- package/dist/headless.js.map +1 -0
- package/dist/index-FVXXKf2v.d.cts +712 -0
- package/dist/index-FVXXKf2v.d.ts +712 -0
- package/dist/index.cjs +533 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +506 -0
- package/dist/index.js.map +1 -0
- package/dist/memoryAdapter-BJjyLR-1.d.cts +24 -0
- package/dist/memoryAdapter-CBu7_3Pp.d.ts +24 -0
- package/dist/react.cjs +5084 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +338 -0
- package/dist/react.d.ts +338 -0
- package/dist/react.js +5029 -0
- package/dist/react.js.map +1 -0
- package/dist/server.cjs +1237 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +219 -0
- package/dist/server.d.ts +219 -0
- package/dist/server.js +1199 -0
- package/dist/server.js.map +1 -0
- package/dist/storage.cjs +440 -0
- package/dist/storage.cjs.map +1 -0
- package/dist/storage.d.cts +147 -0
- package/dist/storage.d.ts +147 -0
- package/dist/storage.js +427 -0
- package/dist/storage.js.map +1 -0
- package/dist/styles.css +1107 -0
- package/dist/types-CiLY0DwB.d.cts +180 -0
- package/dist/types-CiLY0DwB.d.ts +180 -0
- package/package.json +138 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `img-upload-kit` will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.1.0] - 2026-10-31
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **AnimatedImage Component (`img-upload-kit/react`)**:
|
|
13
|
+
- High-performance progressive blur-up display component with GPU-accelerated crossfade.
|
|
14
|
+
- Native `<img>` attribute pass-through and framework-agnostic `as` prop (e.g. `as={Image}` for Next.js with `imageComponentProps`).
|
|
15
|
+
- Hierarchical placeholder fallback cascade: `blurDataUrl` (LQIP) > `blurHash` (client canvas decode) > `blurColor` (gradient SVG).
|
|
16
|
+
- Shimmer highlight animation (`showShimmer`), staggered transition timing, and full `prefers-reduced-motion` compliance.
|
|
17
|
+
- Semantic slot targeting (`data-slot`, `data-state`, `aria-busy`) and unstyled mode support.
|
|
18
|
+
- **Headless Reveal State Machine (`useImageReveal`)**:
|
|
19
|
+
- Headless hook managing progressive reveal states (`isLoaded`, `showPlaceholder`, `hasError`, `imgRef`, `handleLoad`, `handleError`).
|
|
20
|
+
- Guarantees pre-decode verification via `HTMLImageElement.decode()` before revealing sharp asset.
|
|
21
|
+
- Synchronous reset on `srcKey` changes and automatic detection of pre-cached images.
|
|
22
|
+
- **Placeholder Utilities**:
|
|
23
|
+
- `createFallbackBlurSvg`: Generates lightweight inline SVG gradients derived from dominant color.
|
|
24
|
+
- `resolveMeaningfulColor`: Filters out generic neutral/black/white database defaults and validates color tokens.
|
|
25
|
+
- `resolvePlaceholderUrl`: Deterministically selects the best available placeholder asset.
|
|
26
|
+
|
|
27
|
+
### Changed & Hardened
|
|
28
|
+
|
|
29
|
+
- **Server Validation Error Mapping**:
|
|
30
|
+
- Updated `createUploadHttpHandler.POST` to map client validation errors (file size limits, unsupported MIME types, missing file payloads) directly to HTTP 400 Bad Request instead of internal server errors.
|
|
31
|
+
- **Collision Retry Discrimination**:
|
|
32
|
+
- Fixed collision detection in `uploadRetry.ts` to strictly match `already exists` and `EEXIST` error codes without falsely triggering on `does not exist` messages.
|
|
33
|
+
- **Sharp Mobile EXIF Auto-Rotation**:
|
|
34
|
+
- Added `.rotate()` during Sharp pipeline initialization in `imageOptimizer.ts` to automatically orient smartphone and digital camera uploads according to EXIF orientation metadata.
|
|
35
|
+
- **Transparent PNG Color Extraction**:
|
|
36
|
+
- Hardened `colorHistogram.ts` to ignore pixels with `alpha < 8`, preventing transparent PNG graphics from skewing dominant edge colors toward black or white.
|
|
37
|
+
- **BlurHash & SVG Detection**:
|
|
38
|
+
- Enhanced SVG validation in `blurGenerator.ts` to perform case-insensitive checks across the initial 1024 bytes and clamped BlurHash components to valid ranges (1–9).
|
|
39
|
+
- **Storage Parity & Upsert Semantics**:
|
|
40
|
+
- Implemented `deleteByUrl` in `createLocalFsStorageAdapter` for parity with cloud storage providers.
|
|
41
|
+
- Aligned default `upsert` semantics to `true` across all adapters for consistent overwrite behavior.
|
|
42
|
+
- **React Pure State Updaters & Controlled Value Sync**:
|
|
43
|
+
- Refactored `useMultiImageState.ts` and `useSingleImageState.ts` to maintain synchronous `previewsRef` snapshots.
|
|
44
|
+
- Removed side-effect dispatches (`emitImmediateChange`) from inside `setPreviews` functional updaters for strict React 18/19 concurrent rendering purity.
|
|
45
|
+
- Properly reset uploaded data and blur previews when single uploader `value` transitions to `null`.
|
|
46
|
+
- **Batch Preemption Clearing in Multi-Upload**:
|
|
47
|
+
- Fixed retention window in `MultiImageUpload.tsx`: active batch references are now explicitly cleared when an in-flight retention timer is preempted by a new upload batch.
|
|
48
|
+
|
|
49
|
+
## [1.0.0] - 2026-10-31
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- **Core Types & Utilities (`img-upload-kit`)**:
|
|
54
|
+
- Universal project-agnostic visual role presets (`thumbnail`, `avatar`, `card`, `cover`, `hero`, `logo`, `original`).
|
|
55
|
+
- `defineUploadPresets` helper and `UploadKitRegister` TypeScript module augmentation for global preset autocomplete.
|
|
56
|
+
- Bilingual (`en` / `ar`) file validation, quality level presets, byte formatters, and RGB/HEX color utilities.
|
|
57
|
+
- **Server Optimization & Upload Pipeline (`img-upload-kit/server`)**:
|
|
58
|
+
- `createImageUploader` with preset inheritance, path sanitization, SHA-256 content hashing, retry handling, and `AsyncSemaphore` concurrency control.
|
|
59
|
+
- `createUploadHttpHandler` returning Web Standard `POST` and `DELETE` Fetch handlers (`Request` -> `Response`) compatible with Next.js App Router, Remix, React Router, Hono, SvelteKit, and Node servers.
|
|
60
|
+
- `optimizeImage` powered by Sharp with smart AVIF size-guarding (preventing file bloat on pre-compressed JPEGs), WebP fallback, and SVG passthrough.
|
|
61
|
+
- `generateBlurPlaceholder` generating compact WebP LQIP data URLs, optional BlurHash strings, and 4-edge weighted dominant color histograms.
|
|
62
|
+
- **Pluggable Storage Adapters (`img-upload-kit/storage`)**:
|
|
63
|
+
- `StorageAdapter` contract and `createStorageAdapter` authoring helper.
|
|
64
|
+
- Built-in `createSupabaseStorageAdapter`, `createS3StorageAdapter` (AWS S3, Cloudflare R2, MinIO, DigitalOcean Spaces), `createLocalFsStorageAdapter` (with path-traversal protection), and `createMemoryStorageAdapter`.
|
|
65
|
+
- `onUploadRecord` hook for persisting upload metadata to Prisma, Drizzle, or analytics pipelines.
|
|
66
|
+
- **Blur Manifest Service (`img-upload-kit/blur`)**:
|
|
67
|
+
- `BlurService` and `createBlurService` with bounded `LRUCache`, normalized URL/path lookup, and SVG fallback placeholders.
|
|
68
|
+
- **Pure React UI & Styles (`img-upload-kit/react`, `img-upload-kit/styles.css`)**:
|
|
69
|
+
- Framework-agnostic `<ImageUploadProvider>`, `<ImageUpload>`, `<SingleImageUpload>`, `<MultiImageUpload>`, `<BlurImage>`, and `<QualitySelector>`.
|
|
70
|
+
- First-class English & Arabic (`dir="rtl"`) localization, custom `renderImage` adapter slot, and themeable `--iuk-*` CSS custom properties.
|
|
71
|
+
- **Headless Hooks & Primitives (`img-upload-kit/headless`)**:
|
|
72
|
+
- Unstyled controllers: `HeadlessSingleUpload`, `HeadlessMultiUpload`, `useSingleUpload`, `useMultiUpload`, `useImageUpload`, `useDropzone`, `useFilePreview`.
|
|
73
|
+
- **Blur Manifest CLI (`img-upload-kit/cli`)**:
|
|
74
|
+
- `img-upload-kit` / `blur-cli` binaries (`generate`, `validate`, `sync`, `cleanup`, `health`, `inspect`) for build-time `blur-hashes.json` generation.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mo Elsherbiny (https://www.buildwithsherbo.com/)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,259 @@
|
|
|
1
|
-
#
|
|
1
|
+
# img-upload-kit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Production-grade, zero-lock-in image upload and processing framework with **Sharp AVIF/WebP optimization**, **smart size-guarding**, **WebP LQIP & BlurHash placeholders**, **4-edge dominant color extraction**, **pluggable storage adapters**, **4 styling tiers**, and the **`<AnimatedImage />` blur-up display component** for React & Next.js.
|
|
4
|
+
|
|
5
|
+
[](https://upload-kit.buildwithsherbo.com)
|
|
6
|
+
[](https://www.npmjs.com/package/img-upload-kit)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
[](https://github.com/MoElsherbiny/image-upload-kit-studio)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Interactive Studio & Live Demo
|
|
13
|
+
|
|
14
|
+
Experience the full pipeline live in your browser:
|
|
15
|
+
|
|
16
|
+
👉 **[upload-kit.buildwithsherbo.com](https://upload-kit.buildwithsherbo.com)**
|
|
17
|
+
|
|
18
|
+
- **Watch Code Stream Live**: Synchronized Shiki IDE writing `<AnimatedImage />`, upload route, and UI code while the live preview mutates at 60 FPS.
|
|
19
|
+
- **4-Layer Placeholder Lab**: Side-by-side comparison of **4-Edge Ambient Color** (0 B runtime), **Client BlurHash** (~28 chars), **WebP LQIP Data URL** (<1 KB), and the final **Sharp AVIF/WebP** asset.
|
|
20
|
+
- **Sharp Size-Guard Sandbox**: Drop any image to inspect real-time byte savings, dimensions, and color extraction.
|
|
21
|
+
- **Studio Source Code**: Open source at [`MoElsherbiny/image-upload-kit-studio`](https://github.com/MoElsherbiny/image-upload-kit-studio).
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## The Problems Solved
|
|
26
|
+
|
|
27
|
+
1. **Pipeline Fragmentation**: Eliminates stitching separate libraries for file validation, resizing, AVIF/WebP conversion, placeholder generation, storage uploading, and UI forms. Provides one unified pipeline from browser dropzone to CDN.
|
|
28
|
+
2. **AVIF Bloat on Pre-compressed JPEGs**: Naive AVIF converters expand pre-compressed inputs by 30–80%. Our **Sharp Size-Guard** evaluates output bytes against source size, scales quality proportionally, and falls back to WebP or passthrough.
|
|
29
|
+
3. **Cumulative Layout Shift (CLS)**: Eliminates layout shifts by generating instant **WebP LQIP data URLs** (<8KB), client-decoded **BlurHash** strings, and **4-edge ambient colors** before full-resolution assets load.
|
|
30
|
+
4. **Storage Vendor Lock-in**: Decouples application logic from cloud vendors via a uniform `StorageAdapter` contract across AWS S3, Cloudflare R2, Supabase, Local Filesystem, or In-Memory (testing).
|
|
31
|
+
5. **UI Styling Lock-in**: Provides 4 styling tiers: default `--iuk-*` CSS tokens, `unstyled` mode with Tailwind/CSS Modules, partial slot renderers, or 100% headless hooks and primitives.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# npm
|
|
39
|
+
npm install img-upload-kit
|
|
40
|
+
# pnpm
|
|
41
|
+
pnpm add img-upload-kit
|
|
42
|
+
# yarn
|
|
43
|
+
yarn add img-upload-kit
|
|
44
|
+
# bun
|
|
45
|
+
bun add img-upload-kit
|
|
46
|
+
|
|
47
|
+
# Optional peer dependencies (only if using React or Headless UI):
|
|
48
|
+
npm install react react-dom
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> **Requirements**: Node.js `>= 18.17.0` on the server for `sharp` and Web Fetch `Request`/`FormData` APIs. React `>= 18.0.0` for UI components.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Subpath Exports Reference
|
|
56
|
+
|
|
57
|
+
| Subpath | Environment | Key Exports |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| `img-upload-kit` | Universal | `STANDARD_PRESETS`, `defineUploadPresets`, types, color utils, validators, `createMemoryStorageAdapter`, `decodeBlurHashToDataURL`, `getBlurData` |
|
|
60
|
+
| `img-upload-kit/server` | Node.js | `createImageUploader`, `createUploadHttpHandler`, `optimizeImage`, `generateBlurPlaceholder` |
|
|
61
|
+
| `img-upload-kit/storage` | Node / Edge | `createStorageAdapter`, `createS3StorageAdapter`, `createSupabaseStorageAdapter`, `createLocalFsStorageAdapter` |
|
|
62
|
+
| `img-upload-kit/blur` | Universal | `BlurService`, `createBlurService`, `decodeBlurHashToDataURL`, `LRUCache` |
|
|
63
|
+
| `img-upload-kit/react` | React Client | `<ImageUploadProvider>`, `<SingleImageUpload>`, `<MultiImageUpload>`, `<AnimatedImage>`, `<BlurImage>` |
|
|
64
|
+
| `img-upload-kit/headless` | React Client | `<HeadlessSingleUpload>`, `<HeadlessMultiUpload>`, `useSingleUpload`, `useMultiUpload`, `useDropzone` |
|
|
65
|
+
| `img-upload-kit/cli` | Node CLI | CLI binaries (`img-upload-kit` / `blur-cli`) for build-time manifest generation |
|
|
66
|
+
| `img-upload-kit/styles.css` | CSS | Complete stylesheet powered by `--iuk-*` design tokens (supports dark mode & RTL) |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Universal Presets Reference
|
|
71
|
+
|
|
72
|
+
Built-in visual role presets balance dimension constraints, compression quality, and blur generation:
|
|
73
|
+
|
|
74
|
+
| Preset | Max Dimensions | Max Size | Quality | Allowed Formats | Blur / Notes |
|
|
75
|
+
| --- | --- | --- | --- | --- | --- |
|
|
76
|
+
| `thumbnail` | `600x600` | 5 MB | Q78 | JPEG, PNG, WebP, AVIF | Medium quality, compact size |
|
|
77
|
+
| `avatar` | `512x512` | 5 MB | Q80 | JPEG, PNG, WebP, AVIF | Medium quality, square profile |
|
|
78
|
+
| `card` | `1200x900` | 8 MB | Q85 | JPEG, PNG, WebP, AVIF, SVG | High quality content cards |
|
|
79
|
+
| `cover` | `1920x1080` | 10 MB | Q88 | JPEG, PNG, WebP, AVIF, SVG | High quality wide banners |
|
|
80
|
+
| `hero` | `2560x1440` | 15 MB | Q92 | JPEG, PNG, WebP, AVIF, SVG | Maximum detail for hero headers |
|
|
81
|
+
| `logo` | `800x800` | 5 MB | Q90 | JPEG, PNG, WebP, AVIF, SVG | Vector SVG passthrough & sharp edges |
|
|
82
|
+
| `original` | `4096x4096` | 25 MB | Q95 | JPEG, PNG, WebP, AVIF, SVG | Preserves maximum master fidelity |
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { defineUploadPresets } from 'img-upload-kit';
|
|
86
|
+
|
|
87
|
+
export const presets = defineUploadPresets({
|
|
88
|
+
'product-gallery': {
|
|
89
|
+
folder: 'catalog/products',
|
|
90
|
+
maxDimensions: { width: 1600, height: 1600 },
|
|
91
|
+
maxSizeMB: 10,
|
|
92
|
+
quality: 85,
|
|
93
|
+
includeBlurHash: true,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Subpath Usage Guides
|
|
101
|
+
|
|
102
|
+
### 1. Server Uploader & Route Handler (`/server` & `/storage`)
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
// app/api/upload/route.ts (Next.js App Router, Remix, Hono, SvelteKit)
|
|
106
|
+
import { createImageUploader, createUploadHttpHandler } from 'img-upload-kit/server';
|
|
107
|
+
import { createS3StorageAdapter } from 'img-upload-kit/storage';
|
|
108
|
+
|
|
109
|
+
const storage = createS3StorageAdapter({
|
|
110
|
+
publicBaseUrl: 'https://cdn.example.com',
|
|
111
|
+
defaultBucket: 'media',
|
|
112
|
+
putObject: (input) => s3.send(new PutObjectCommand(input)),
|
|
113
|
+
deleteObject: (input) => s3.send(new DeleteObjectCommand(input)),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
export const uploader = createImageUploader({ storage, defaultBucket: 'media' });
|
|
117
|
+
export const { POST, DELETE } = createUploadHttpHandler({ uploader });
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 2. Client Provider & Upload UI (`/react` & `/styles.css`)
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
import { useState } from 'react';
|
|
124
|
+
import { ImageUploadProvider, SingleImageUpload, MultiImageUpload } from 'img-upload-kit/react';
|
|
125
|
+
import type { UploadedImageData } from 'img-upload-kit';
|
|
126
|
+
import 'img-upload-kit/styles.css';
|
|
127
|
+
|
|
128
|
+
export function MediaForm() {
|
|
129
|
+
const [avatar, setAvatar] = useState<UploadedImageData | null>(null);
|
|
130
|
+
const [gallery, setGallery] = useState<UploadedImageData[]>([]);
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<ImageUploadProvider endpoint="/api/upload" locale="en">
|
|
134
|
+
<SingleImageUpload preset="avatar" label="Avatar" value={avatar} onChange={setAvatar} />
|
|
135
|
+
<MultiImageUpload preset="card" label="Gallery" value={gallery} onChange={setGallery} maxImages={10} />
|
|
136
|
+
</ImageUploadProvider>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 3. Build-Time Blur Manifests & Runtime Lookup (`/cli` & `/blur`)
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
npx img-upload-kit generate --dir ./public/images --out ./src/blur-manifest.json
|
|
145
|
+
```
|
|
146
|
+
```ts
|
|
147
|
+
import { createBlurService } from 'img-upload-kit/blur';
|
|
148
|
+
import manifest from './blur-manifest.json';
|
|
149
|
+
|
|
150
|
+
const blurService = createBlurService({ manifest });
|
|
151
|
+
const { color, blur: blurDataUrl } = blurService.getProgressiveData('/images/hero.avif');
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## The 4 Styling Tiers
|
|
157
|
+
|
|
158
|
+
Customize UI components cleanly at any level of abstraction:
|
|
159
|
+
|
|
160
|
+
### Tier 1: CSS Variables (`--iuk-*`) & Automatic Dark Mode
|
|
161
|
+
```css
|
|
162
|
+
:root {
|
|
163
|
+
--iuk-primary: #4f46e5;
|
|
164
|
+
--iuk-primary-hover: #4338ca;
|
|
165
|
+
--iuk-radius-md: 14px;
|
|
166
|
+
}
|
|
167
|
+
.dark { --iuk-primary: #818cf8; --iuk-surface: #0f172a; }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Tier 2: Unstyled Mode with `classNames` & `data-[state=*]` (Tailwind CSS)
|
|
171
|
+
```tsx
|
|
172
|
+
<SingleImageUpload
|
|
173
|
+
unstyled value={avatar} onChange={setAvatar}
|
|
174
|
+
classNames={{
|
|
175
|
+
dropzone: 'rounded-2xl border-2 border-dashed p-6 text-center data-[state=dragging]:border-indigo-500',
|
|
176
|
+
browseButton: 'rounded-lg bg-indigo-600 px-4 py-2 text-white',
|
|
177
|
+
}}
|
|
178
|
+
/>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Tier 3: Partial Slot Renderers
|
|
182
|
+
```tsx
|
|
183
|
+
<SingleImageUpload
|
|
184
|
+
value={avatar} onChange={setAvatar}
|
|
185
|
+
renderDropzone={(ctx) => (
|
|
186
|
+
<div {...ctx.dropzoneProps} className="p-8 border border-dashed rounded-xl">
|
|
187
|
+
<button type="button" onClick={ctx.openFileDialog}>Select Photo</button>
|
|
188
|
+
</div>
|
|
189
|
+
)}
|
|
190
|
+
/>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Tier 4: Headless Hooks & Primitives (`/headless`)
|
|
194
|
+
```tsx
|
|
195
|
+
import { HeadlessSingleUpload, useDropzone } from 'img-upload-kit/headless';
|
|
196
|
+
|
|
197
|
+
<HeadlessSingleUpload preset="avatar" value={avatar} onChange={setAvatar}>
|
|
198
|
+
{(ctrl) => (
|
|
199
|
+
<div {...ctrl.getDropzoneProps()}>
|
|
200
|
+
<input {...ctrl.getInputProps()} />
|
|
201
|
+
{ctrl.preview ? <img src={ctrl.preview} alt="" /> : <button type="button" onClick={ctrl.openFileDialog}>Upload</button>}
|
|
202
|
+
</div>
|
|
203
|
+
)}
|
|
204
|
+
</HeadlessSingleUpload>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## AnimatedImage (Blur-Up Display Component)
|
|
210
|
+
|
|
211
|
+
`<AnimatedImage />` provides an instant blur-up placeholder crossfade to full resolution without layout shift:
|
|
212
|
+
|
|
213
|
+
```tsx
|
|
214
|
+
import { AnimatedImage } from 'img-upload-kit/react';
|
|
215
|
+
import 'img-upload-kit/styles.css';
|
|
216
|
+
|
|
217
|
+
// 1. Direct shorthand with UploadedImageData:
|
|
218
|
+
<AnimatedImage image={img} showShimmer />
|
|
219
|
+
|
|
220
|
+
// 2. Explicit props in standard React:
|
|
221
|
+
<AnimatedImage
|
|
222
|
+
src={img.url}
|
|
223
|
+
alt={img.altText || 'Product'}
|
|
224
|
+
blurDataUrl={img.blurDataUrl}
|
|
225
|
+
blurHash={img.blurHash}
|
|
226
|
+
blurColor={img.blurColor}
|
|
227
|
+
width={800}
|
|
228
|
+
height={600}
|
|
229
|
+
showShimmer
|
|
230
|
+
/>
|
|
231
|
+
|
|
232
|
+
// 3. Next.js App Router (as={Image}):
|
|
233
|
+
import Image from 'next/image';
|
|
234
|
+
<div style={{ position: 'relative', aspectRatio: '16/9' }}>
|
|
235
|
+
<AnimatedImage image={img} as={Image} fill sizes="100vw" imageComponentProps={{ priority: true, quality: 80 }} />
|
|
236
|
+
</div>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Documentation
|
|
242
|
+
|
|
243
|
+
- [AI Agent Contributor Guide (`AGENTS.md`)](./AGENTS.md) — Architecture, guardrails, and styling tiers for AI agents.
|
|
244
|
+
- [Custom Styling & Headless Guide (`docs/CUSTOM_STYLING.md`)](./docs/CUSTOM_STYLING.md) — Theming, slots, and headless recipes.
|
|
245
|
+
- [AnimatedImage & Blur-Up Guide (`docs/ANIMATED_IMAGE.md`)](./docs/ANIMATED_IMAGE.md) — Progressive reveal component architecture.
|
|
246
|
+
- [Architecture & Internals (`docs/ARCHITECTURE.md`)](./docs/ARCHITECTURE.md) — Sharp size-guarding and 4-edge color math.
|
|
247
|
+
- [Framework & Storage Recipes (`docs/FRAMEWORK_RECIPES.md`)](./docs/FRAMEWORK_RECIPES.md) — Next.js, Hono, Remix, S3, Supabase recipes.
|
|
248
|
+
- [Interactive Studio Code](https://github.com/MoElsherbiny/image-upload-kit-studio) — Source for the live demo playground.
|
|
249
|
+
- [Contributing Guide (`CONTRIBUTING.md`)](./CONTRIBUTING.md) & [Changelog (`CHANGELOG.md`)](./CHANGELOG.md).
|
|
250
|
+
|
|
251
|
+
## License & Author
|
|
252
|
+
|
|
253
|
+
**Author & Identity**: [buildwithsherbo](https://www.buildwithsherbo.com/) — Mohamed Elsherbiny
|
|
254
|
+
- **Website**: [buildwithsherbo.com](https://www.buildwithsherbo.com/)
|
|
255
|
+
- **Live Demo**: [upload-kit.buildwithsherbo.com](https://upload-kit.buildwithsherbo.com)
|
|
256
|
+
- **GitHub**: [@MoElsherbiny](https://github.com/MoElsherbiny)
|
|
257
|
+
- **Email**: [mo@buildwithsherbo.com](mailto:mo@buildwithsherbo.com)
|
|
258
|
+
|
|
259
|
+
Distributed under the [MIT License](./LICENSE).
|