Counter
superdesignCC0-1.0Prompt onlyLanding · landingGradient · gradientProfessional · professionalLuxury · luxuryLight · lightMedium · mediumNeutral · neutralMuted · mutedSerif · serifGeneral · general
A premium, high-performance rolling counter component with smooth mechanical-style transitions. Features automatic digit detection, customizable font sizes, and sleek gradient overlays for a professional 'odometer' effect. Built with Framer Motion for precise easing and spring physics. Source: ReactBits Category: Components. Prompt author listed upstream: Jason Zhou.
NNorthwind
Product · PricingSpecimen
Make the work feel finished
One interface, dressed in this style. Compare type, color, radius, and density.
128
Active
4.8
Score
12ms
Edge
Attribution
"Counter" from https://github.com/superdesigndev/superdesign-prompts (CC0-1.0). © Superdesign. prompts/counter @ 6c6090694520.
https://github.com/superdesigndev/superdesign-prompts · prompts/counter · 6c6090694520 · © Superdesign
Prompt
You are given a task to integrate an existing React component in the codebase
~~~/README.md
# RollingCounter
A premium, high-performance rolling counter component with smooth mechanical-style transitions. Features automatic digit detection, customizable font sizes, and sleek gradient overlays for a professional 'odometer' effect.
## Dependencies
- `framer-motion`: `^11.0.0`
- `lucide-react`: `latest` (for demo icons)
## Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `number` | - | The numeric value to display. |
| `fontSize` | `number` | `100` | Font size in pixels. |
| `padding` | `number` | `0` | Vertical padding inside each digit container. |
| `places` | `PlaceValue[]` | - | Custom array of place values (e.g., `[100, 10, 1, '.', 0.1]`). |
| `gap` | `number` | `8` | Gap between digits. |
| `borderRadius` | `number` | `4` | Border radius of the counter container. |
| `horizontalPadding` | `number` | `8` | Horizontal padding of the counter container. |
| `textColor` | `string` | `'inherit'` | Text color. |
| `fontWeight` | `CSSProperties['fontWeight']` | `'inherit'` | Font weight. |
| `gradientHeight` | `number` | `16` | Height of the gradient overlay. |
| `gradientFrom` | `string` | `'hsl(var(--background))'` | Start color of the gradient. |
## Usage
```tsx
import { RollingCounter } from '@/sd-components/7d2b2a3f-6411-4777-94ce-a18ef1ceca98';
export default function MyComponent() {
return (
<RollingCounter
value={5432.1}
fontSize={64}
textColor="#1A1A1B"
gradientFrom="#F9F9F9"
/>
);
}
```
~~~
~~~/src/App.tsx
/**
* Demo for RollingCounter Component
*
* Shows a minimalist showcase of the rolling counter effect.
* The counter value increments automatically to demonstrate the motion.
*/
import React, { useState, useEffect } from 'react';
import { RollingCounter } from './Component';
import { RotateCcw } from 'lucide-react';
export default function App() {
const [value, setValue] = useState(1234.5);
useEffect(() => {
const timer = setInterval(() => {
setValue(v => parseFloat((v + 1.1).toFixed(1)));
}, 2000);
return () => clearInterval(timer);
}, []);
const resetValue = () => setValue(1234.5);
return (
<div className="min-h-screen bg-[#F9F9F9] flex flex-col items-center justify-center p-20">
{/* Title as per design guidelines */}
<h1 className="mb-12 text-sm uppercase tracking-widest text-muted-foreground font-medium">
Rolling Counter
</h1>
{/* Main Showcase Container */}
<div className="bg-white p-20 rounded-[40px] shadow-[0_40px_80px_rgba(0,0,0,0.05)] flex flex-col items-center gap-12">
<RollingCounter
value={value}
fontSize={120}
textColor="hsl(var(--foreground))"
fontWeight={600}
gradientFrom="white"
gradientHeight={30}
gap={4}
/>
{/* Reply/Action Button as per guidelines */}
<button
onClick={resetValue}
className="flex items-center gap-2 px-6 py-3 bg-foreground text-background rounded-full hover:opacity-90 transition-opacity font-medium text-sm"
>
<RotateCcw className="w-4 h-4" />
Reset Counter
</button>
</div>
{/* Subtitle / Description */}
<p className="mt-12 text-muted-foreground text-center max-w-md text-sm leading-relaxed">
A precision mechanical-style animation using Framer Motion
with automatic place value detection and smooth transitions.
</p>
</div>
);
}
~~~
~~~/package.json
{
"name": "rolling-counter",
"description": "A premium mechanical rolling counter component with smooth Framer Motion animations.",
"dependencies": {
"framer-motion": "^11.0.0",
"lucide-react": "latest",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
~~~
~~~/src/Component.tsx
/**
* RollingCounter Component
*
* A high-performance, mechanical-style rolling counter using Framer Motion.
* It features smooth digit transitions, automatic place value detection,
* and customizable styling including gradient overlays for a polished look.
*/
import { MotionValue, motion, useSpring, useTransform } from 'framer-motion';
import React, { useEffect } from 'react';
type PlaceValue = number | '.';
interface NumberProps {
mv: MotionValue<number>;
number: number;
height: number;
}
function Number({ mv, number, height }: NumberProps) {
const y = useTransform(mv, latest => {
const placeValue = latest % 10;
const offset = (10 + number - placeValue) % 10;
let memo = offset * height;
if (offset > 5) {
memo -= 10 * height;
}
return memo;
});
const baseStyle: React.CSSProperties = {
position: 'absolute',
inset: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
};
return <motion.span style={{ ...baseStyle, y }}>{number}</motion.span>;
}
interface DigitProps {
place: PlaceValue;
value: number;
height: number;
digitStyle?: React.CSSProperties;
}
function Digit({ place, value, height, digitStyle }: DigitProps) {
// Decimal point digit
if (place === '.') {
return (
<span
className="relative inline-flex items-center justify-center"
style={{ height, width: 'fit-content', ...digitStyle }}
>
.
</span>
);
}
// Numeric digit
const valueRoundedToPlace = Math.floor(value / place);
const animatedValue = useSpring(valueRoundedToPlace, {
damping: 20,
stiffness: 100,
mass: 1
});
useEffect(() => {
animatedValue.set(valueRoundedToPlace);
}, [animatedValue, valueRoundedToPlace]);
const defaultStyle: React.CSSProperties = {
height,
position: 'relative',
width: '1ch',
fontVariantNumeric: 'tabular-nums'
};
return (
<span className="relative inline-flex overflow-hidden" style={{ ...defaultStyle, ...digitStyle }}>
{Array.from({ length: 10 }, (_, i) => (
<Number key={i} mv={animatedValue} number={i} height={height} />
))}
</span>
);
}
export interface RollingCounterProps {
/** The numeric value to display */
value: number;
/** Font size in pixels (default: 100) */
fontSize?: number;
/** Vertical padding inside each digit container (default: 0) */
padding?: number;
/**
* Custom array of place values (e.g., [100, 10, 1, '.', 0.1]).
* If omitted, it's automatically detected from value.
*/
places?: PlaceValue[];
/** Gap between digits (default: 8) */
gap?: number;
/** Border radius of the counter container (default: 4) */
borderRadius?: number;
/** Horizontal padding of the counter container (default: 8) */
horizontalPadding?: number;
/** Text color (default: 'inherit') */
textColor?: string;
/** Font weight (default: 'inherit') */
fontWeight?: React.CSSProperties['fontWeight'];
/** Style object for the outer container */
containerStyle?: React.CSSProperties;
/** Style object for the inner counter wrapper */
counterStyle?: React.CSSProperties;
/** Style object for each individual digit span */
digitStyle?: React.CSSProperties;
/** Height of the gradient overlay (default: 16) */
gradientHeight?: number;
/** Start color of the gradient (default: 'black') */
gradientFrom?: string;
/** End color of the gradient (default: 'transparent') */
gradientTo?: string;
/** Custom style for the top gradient */
topGradientStyle?: React.CSSProperties;
/** Custom style for the bottom gradient */
bottomGradientStyle?: React.CSSProperties;
}
/**
* A reusable rolling counter component with smooth mechanical motion.
*/
export function RollingCounter({
value,
fontSize = 100,
padding = 0,
places,
gap = 8,
borderRadius = 4,
horizontalPadding = 8,
textColor = 'inherit',
fontWeight = 'inherit',
containerStyle,
counterStyle,
digitStyle,
gradientHeight = 16,
gradientFrom = 'hsl(var(--background))',
gradientTo = 'transparent',
topGradientStyle,
bottomGradientStyle
}: RollingCounterProps) {
const height = fontSize + padding;
// Automatic place detection if none provided
const derivedPlaces = places || [...value.toString()].map((ch, i, a) => {
if (ch === '.') return '.';
const dotIndex = a.indexOf('.');
const isInteger = dotIndex === -1;
const exponent = isInteger ? a.length - i - 1 : i < dotIndex ? dotIndex - i - 1 : -(i - dotIndex);
return 10 ** exponent;
});
const defaultContainerStyle: React.CSSProperties = {
position: 'relative',
display: 'inline-block'
};
const defaultCounterStyle: React.CSSProperties = {
fontSize,
display: 'flex',
gap,
overflow: 'hidden',
borderRadius,
paddingLeft: horizontalPadding,
paddingRight: horizontalPadding,
lineHeight: 1,
color: textColor,
fontWeight
};
const gradientContainerStyle: React.CSSProperties = {
pointerEvents: 'none',
position: 'absolute',
inset: 0,
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between'
};
const defaultTopGradientStyle: React.CSSProperties = {
height: gradientHeight,
background: `linear-gradient(to bottom, ${gradientFrom}, ${gradientTo})`,
zIndex: 10
};
const defaultBottomGradientStyle: React.CSSProperties = {
height: gradientHeight,
background: `linear-gradient(to top, ${gradientFrom}, ${gradientTo})`,
zIndex: 10
};
return (
<span style={{ ...defaultContainerStyle, ...containerStyle }}>
<span style={{ ...defaultCounterStyle, ...counterStyle }}>
{derivedPlaces.map((place, idx) => (
<Digit
key={`${place}-${idx}`}
place={place}
value={value}
height={height}
digitStyle={digitStyle}
/>
))}
</span>
<span style={gradientContainerStyle}>
<span style={topGradientStyle ?? defaultTopGradientStyle} />
<span style={bottomGradientStyle ?? defaultBottomGradientStyle} />
</span>
</span>
);
}
export default RollingCounter;
~~~
Implementation Guidelines
1. Analyze the component structure, styling, animation implementations
2. Review the component's arguments and state
3. Think through what is the best place to adopt this component/style into the design we are doing
4. Then adopt the component/design to our current system
Help me integrate this into my design
---
"Counter" from https://github.com/superdesigndev/superdesign-prompts (CC0-1.0). © Superdesign. prompts/counter @ 6c6090694520.
powered by Styledex — https://styledex.devTokens
DTCG
{
"$schema": "https://www.designtokens.org/schemas/2025.10/format.json",
"$description": "Counter. \"Counter\" from https://github.com/superdesigndev/superdesign-prompts (CC0-1.0). © Superdesign. prompts/counter @ 6c6090694520.",
"$extensions": {
"dev.styledex": {
"id": "superdesign-counter",
"license": "CC0-1.0",
"attribution": "\"Counter\" from https://github.com/superdesigndev/superdesign-prompts (CC0-1.0). © Superdesign. prompts/counter @ 6c6090694520.",
"banner": "powered by Styledex — https://styledex.dev"
}
},
"color": {
"background": {
"$type": "color",
"$value": "#f9f9f9"
},
"foreground": {
"$type": "color",
"$value": "#1a1a1b"
},
"card": {
"$type": "color",
"$value": "#f9f9f9"
},
"card-foreground": {
"$type": "color",
"$value": "#1a1a1b"
},
"primary": {
"$type": "color",
"$value": "#1a1a1b"
},
"primary-foreground": {
"$type": "color",
"$value": "#fafaf9"
},
"secondary": {
"$type": "color",
"$value": "#e5e5e5"
},
"secondary-foreground": {
"$type": "color",
"$value": "#1a1a1b"
},
"accent": {
"$type": "color",
"$value": "#1a1a1b"
},
"accent-foreground": {
"$type": "color",
"$value": "#fafaf9"
},
"muted": {
"$type": "color",
"$value": "#eaeaea"
},
"muted-foreground": {
"$type": "color",
"$value": "#6a6a6b"
},
"border": {
"$type": "color",
"$value": "#d6d6d6"
},
"destructive": {
"$type": "color",
"$value": "#dc2626"
},
"ring": {
"$type": "color",
"$value": "#1a1a1b"
}
},
"font": {
"sans": {
"$type": "fontFamily",
"$value": [
"ui-sans-serif",
"system-ui",
"sans-serif"
]
},
"serif": {
"$type": "fontFamily",
"$value": [
"ui-serif",
"Georgia",
"serif"
]
},
"mono": {
"$type": "fontFamily",
"$value": [
"ui-monospace",
"SFMono-Regular",
"Menlo",
"monospace"
]
}
},
"radius": {
"base": {
"$type": "dimension",
"$value": "0.5rem"
}
},
"spacing": {
"base": {
"$type": "dimension",
"$value": "1rem"
}
},
"shadow": {
"base": {
"$type": "shadow",
"$value": "0 1px 2px rgba(0,0,0,0.06), 0 8px 24px rgba(0,0,0,0.04)"
}
}
}
CSS
/* powered by Styledex — https://styledex.dev */
/* "Counter" from https://github.com/superdesigndev/superdesign-prompts (CC0-1.0). © Superdesign. prompts/counter @ 6c6090694520. */
:root {
--background: #f9f9f9;
--foreground: #1a1a1b;
--card: #f9f9f9;
--card-foreground: #1a1a1b;
--primary: #1a1a1b;
--primary-foreground: #fafaf9;
--secondary: #e5e5e5;
--secondary-foreground: #1a1a1b;
--accent: #1a1a1b;
--accent-foreground: #fafaf9;
--muted: #eaeaea;
--muted-foreground: #6a6a6b;
--border: #d6d6d6;
--destructive: #dc2626;
--ring: #1a1a1b;
--radius: 0.5rem;
--font-sans: ui-sans-serif, system-ui, sans-serif;
--font-serif: ui-serif, Georgia, serif;
--font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
--spacing: 1rem;
--shadow: 0 1px 2px rgba(0,0,0,0.06), 0 8px 24px rgba(0,0,0,0.04);
}
Tailwind
/* powered by Styledex — https://styledex.dev */
/* "Counter" from https://github.com/superdesigndev/superdesign-prompts (CC0-1.0). © Superdesign. prompts/counter @ 6c6090694520. */
/** Tailwind theme extend for Counter (superdesign-counter) */
module.exports = {
"theme": {
"extend": {
"colors": {
"background": "#f9f9f9",
"foreground": "#1a1a1b",
"card": "#f9f9f9",
"primary": "#1a1a1b",
"primary-foreground": "#fafaf9",
"secondary": "#e5e5e5",
"accent": "#1a1a1b",
"muted": "#eaeaea",
"border": "#d6d6d6",
"destructive": "#dc2626",
"ring": "#1a1a1b"
},
"borderRadius": {
"lg": "0.5rem"
},
"fontFamily": {
"sans": [
"ui-sans-serif",
"system-ui",
"sans-serif"
],
"serif": [
"ui-serif",
"Georgia",
"serif"
],
"mono": [
"ui-monospace",
"SFMono-Regular",
"Menlo",
"monospace"
]
},
"boxShadow": {
"styledex": "0 1px 2px rgba(0,0,0,0.06), 0 8px 24px rgba(0,0,0,0.04)"
}
}
}
};
shadcn
/* powered by Styledex — https://styledex.dev */
/* "Counter" from https://github.com/superdesigndev/superdesign-prompts (CC0-1.0). © Superdesign. prompts/counter @ 6c6090694520. */
@layer base {
:root {
--background: #f9f9f9;
--foreground: #1a1a1b;
--card: #f9f9f9;
--card-foreground: #1a1a1b;
--primary: #1a1a1b;
--primary-foreground: #fafaf9;
--secondary: #e5e5e5;
--secondary-foreground: #1a1a1b;
--accent: #1a1a1b;
--accent-foreground: #fafaf9;
--muted: #eaeaea;
--muted-foreground: #6a6a6b;
--border: #d6d6d6;
--destructive: #dc2626;
--ring: #1a1a1b;
--radius: 0.5rem;
--font-sans: ui-sans-serif, system-ui, sans-serif;
--font-serif: ui-serif, Georgia, serif;
--font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
--spacing: 1rem;
--shadow: 0 1px 2px rgba(0,0,0,0.06), 0 8px 24px rgba(0,0,0,0.04);
}
}