awesome-product-slider
v0.1.4
Published
A clean glass-morphism product slider for React 19 with orbital card display, auto-play, swipe, and keyboard support.
Downloads
457
Maintainers
Readme
awesome-product-slider
A clean, glass-morphism product slider for React 19. Features a two-column editorial layout with a left text panel and a right orbital card display, an animated SVG arc connecting satellite thumbnails, a central floating hero card, quantity stepper, per-item accent colours, auto-play, swipe, and full keyboard support — with zero UI dependencies.
Installation
npm install awesome-product-slideryarn add awesome-product-sliderpnpm add awesome-product-sliderQuick Start
Always import the stylesheet alongside the component — it provides all glass-morphism styles, keyframe animations, the two-column layout, and responsive breakpoints.
import AwesomeProductSlider from 'awesome-product-slider'
import 'awesome-product-slider/style.css'Examples
1. Static Data
Pass a plain JavaScript array directly to the data prop. No network request is made — the slider renders immediately.
You can also swap emoji for image and pass an imported image/SVG (see the sample data in src/data.js).
import AwesomeProductSlider from 'awesome-product-slider'
import 'awesome-product-slider/style.css'
const PRODUCTS = [
{
id: 1,
title: 'Acai Power Bowl',
subtitle: 'Superfood Series',
price: '$12.99',
tag: 'BESTSELLER',
description:
'Organic acai base topped with fresh blueberries, banana slices, granola, and a drizzle of raw honey. Cold-prepped daily.',
accent: '#a78bfa',
bg: 'rgba(167,139,250,0.12)',
emoji: '🫐',
},
{
id: 2,
title: 'Green Detox Smoothie',
subtitle: 'Cleanse Range',
price: '$8.49',
tag: 'NEW',
description:
'Cold-pressed spinach, cucumber, fresh ginger, lemon juice, and a pinch of Himalayan salt. Zero sugar added.',
accent: '#34d399',
bg: 'rgba(52,211,153,0.12)',
emoji: '🥤',
},
{
id: 3,
title: 'Mango Chilli Bowl',
subtitle: 'Tropical Range',
price: '$11.25',
tag: 'SPICY',
description:
'Ripe Alphonso mango, toasted coconut, micro-chilli flakes, and lime zest on a coconut-milk chia base.',
accent: '#fb923c',
bg: 'rgba(251,146,60,0.12)',
emoji: '🥭',
},
{
id: 4,
title: 'Cold Brew Coffee',
subtitle: 'Specialty Coffee',
price: '$5.75',
tag: 'DAILY',
description:
'Single-origin Colombian beans steeped for 20 hours. Served over ice with oat milk. Naturally low acid.',
accent: '#d97706',
bg: 'rgba(217,119,6,0.12)',
emoji: '☕',
},
]
export default function App() {
return (
<AwesomeProductSlider
data={PRODUCTS}
onSlideClick={(item, qty) =>
console.log(`Added ${qty}× ${item.title} to cart`)
}
/>
)
}2. Local JSON File
Import a .json file directly. The bundler resolves it at build time — no fetch, no loading state.
import AwesomeProductSlider from 'awesome-product-slider'
import 'awesome-product-slider/style.css'
import products from './data/products.json'
export default function App() {
return (
<AwesomeProductSlider
data={products}
onSlideClick={(item, qty) =>
alert(`🛒 ${qty}× ${item.title} — ${item.price}`)
}
/>
)
}Your products.json should follow this shape:
[
{
"id": 1,
"title": "Acai Power Bowl",
"subtitle": "Superfood Series",
"price": "$12.99",
"tag": "BESTSELLER",
"description": "Organic acai base topped with fresh blueberries and granola.",
"accent": "#a78bfa",
"bg": "rgba(167,139,250,0.12)",
"emoji": "🫐"
},
{
"id": 2,
"title": "Green Detox Smoothie",
"subtitle": "Cleanse Range",
"price": "$8.49",
"tag": "NEW",
"description": "Cold-pressed spinach, cucumber, ginger and lemon. Zero sugar.",
"accent": "#34d399",
"bg": "rgba(52,211,153,0.12)",
"emoji": "🥤"
}
]3. REST API Endpoint
Pass a URL string to data. The component fetches on mount, shows a loading spinner while the request is in flight, and renders automatically on success. The response must be a JSON array or an object with a data array field.
import AwesomeProductSlider from 'awesome-product-slider'
import 'awesome-product-slider/style.css'
export default function App() {
return (
<AwesomeProductSlider
data="https://api.example.com/products/featured"
onSlideClick={(item, qty) => {
fetch('/api/cart', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ productId: item.id, quantity: qty }),
})
}}
/>
)
}The API must return either a flat array:
[
{ "id": 1, "title": "...", "accent": "#a78bfa", "emoji": "🫐", ... }
]or an envelope object:
{
"data": [
{ "id": 1, "title": "...", "accent": "#a78bfa", "emoji": "🫐", ... }
]
}Item Shape
Each object in the data array must conform to the following shape.
| Property | Type | Required | Description |
|---------------|-------------------|:--------:|-------------|
| id | string \| number | ✅ | Unique identifier used as the React key. |
| title | string | ✅ | Primary product name shown as the large heading in the left panel. |
| emoji | string | ✅ | Single emoji rendered in the central hero card and satellite thumbnail. |
| accent | string | ✅ | CSS hex colour applied to the eyebrow tag, price label, satellite ring, CTA button, footer dot, and card glow. |
| bg | string | ✅ | CSS colour value used as the background fill for the satellite card and the central hero card (use a low-opacity version of accent, e.g. "rgba(167,139,250,0.12)"). |
| subtitle | string | — | Small label shown above the title in the left panel (e.g. category or range name). |
| price | string | — | Price string displayed beneath the title (e.g. "$12.99", "From $49/mo"). Any string is accepted. |
| tag | string | — | Short badge label shown in the top-right of the hero card and as the eyebrow in the left panel (e.g. "BESTSELLER", "NEW"). |
| description | string | — | Body copy shown in the left panel beneath the price. |
Component Props
| Prop | Type | Required | Default | Description |
|----------------|-------------------------------------------|:--------:|:-------:|-------------|
| data | Item[] \| string | ✅ | — | Either a static array of item objects (or imported JSON), or a URL string pointing to a JSON REST endpoint. When a URL is passed the component fetches on mount, shows a loading spinner, and renders on success. The API must return a JSON array or { data: [] }. |
| onSlideClick | (item: Item, qty: number) => void | — | — | Called when the user clicks the Order Now button on the active slide. Receives the full active item object and the current quantity from the stepper. Use this to add to cart, push to a router, fire analytics, and so on. |
Built-In Interactions
| Interaction | Behaviour |
|----------------------|-----------|
| Auto-play | Advances to the next slide every 4.5 seconds automatically. |
| Hover to pause | Auto-play pauses when the cursor enters the component and does not resume until the next mount cycle. |
| Satellite click | Clicking any satellite thumbnail in the orbit jumps directly to that slide and pauses auto-play. |
| Prev / Next | Clicking the Previous or Next text buttons in the left panel navigates one slide and pauses auto-play. |
| Keyboard | ArrowRight advances, ArrowLeft goes back. Both pause auto-play. Handlers are attached to window. |
| Quantity stepper | The − and + buttons adjust the quantity (minimum 1). Quantity resets to 1 on every slide change. |
| Order Now | Fires onSlideClick(item, qty) and logs the order to the console. |
| Loading state | When data is a URL, a centred spinner with the label "Loading menu…" is shown until the fetch resolves. |
Keyboard Navigation
| Key | Action |
|--------------|------------------------|
| ArrowRight | Jump to next slide |
| ArrowLeft | Jump to previous slide |
Responsive Behaviour
On viewports narrower than 860px the two-column grid collapses to a single column, navigation links in the navbar are hidden, and the orbit stage scales down from 500 × 500px to 340 × 340px. The central hero card scales from 220 × 220px to 150 × 150px. No configuration is required.
License
MIT © 2026
