npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

retro-floppy

v1.0.0

Published

Retro Floppy - A beautiful, interactive 3.5" floppy disk React component for retro-themed UIs

Downloads

15

Readme

Features

  • Highly Customizable - Size variants, color themes, and structured label content
  • Interactive - Hover animations plus click and double-click handlers
  • Flexible Sizing - From tiny (60px) to hero (600px) or custom pixel values
  • Performant - Optimized for rendering multiple instances in lists/grids
  • Accessible - ARIA labels and keyboard navigation support
  • TypeScript - Full type definitions included
  • Zero Dependencies - Only requires React

Installation

npm install retro-floppy
# or
yarn add retro-floppy
# or
pnpm add retro-floppy

Quick Start

import { FloppyDisk } from 'retro-floppy';
import 'retro-floppy/dist/retro-floppy.css';

function App() {
  return (
    <FloppyDisk
      size="medium"
      label={{
        name: 'Second Reality',
        author: 'Future Crew',
        year: '1993',
        description: 'Legendary 1993 demo by Future Crew',
        type: 'ZIP',
        size: '1.44 MB',
      }}
      onClick={() => console.log('Disk clicked!')}
    />
  );
}

Usage Examples

Basic Usage

<FloppyDisk
  size="small"
  label={{
    name: 'My Application',
    author: 'Version 1.0',
  }}
  diskType="HD"
  capacity="1.44 MB"
/>

Software Library Grid

const applications = [
  { id: 1, name: 'Photoshop 3.0', author: 'Adobe Systems' },
  { id: 2, name: 'Doom', author: 'id Software' },
  { id: 3, name: 'Windows 95', author: 'Microsoft' },
];

function SoftwareLibrary() {
  const [selected, setSelected] = useState<number | null>(null);

  return (
    <div
      style={{
        display: 'grid',
        gridTemplateColumns: 'repeat(auto-fill, minmax(140px, 1fr))',
        gap: '20px',
      }}
    >
      {applications.map((app) => (
        <FloppyDisk
          key={app.id}
          size="small"
          label={{
            name: app.name,
            author: app.author,
          }}
          selected={selected === app.id}
          onClick={() => setSelected(app.id)}
          onDoubleClick={() => alert(`Launching ${app.name}`)}
        />
      ))}
    </div>
  );
}

Custom Theme

<FloppyDisk
  size="large"
  label={{
    name: 'Custom Theme',
  }}
  theme={{
    diskColor: '#1a1a1a',
    slideColor: '#ffd700',
    backgroundColor: '#f0f0f0',
    labelColor: '#ffffcc',
    labelTextColor: '#333333',
  }}
/>

List View with Compact Variant

<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
  {files.map((file) => (
    <div
      key={file.id}
      style={{ display: 'flex', alignItems: 'center', gap: '15px' }}
    >
      <FloppyDisk size="tiny" variant="compact" label={{ name: file.name }} />
      <span>{file.name}</span>
      <span>{file.size}</span>
    </div>
  ))}
</div>

Theming

The disk uses a default theme that works well in both light and dark UIs. You can customize colors via the theme prop to match your design.

Theme Showcase

The component includes several built-in theme presets for different aesthetics:

Light Theme (Default)

Classic floppy disk appearance optimized for light backgrounds.

import { FloppyDisk, LIGHT_FLOPPY_THEME } from 'retro-floppy';

<FloppyDisk
  label={{ name: 'My App', author: 'Developer' }}
  theme={LIGHT_FLOPPY_THEME}
/>;

Dark Theme

Sleek dark appearance optimized for dark backgrounds.

import { FloppyDisk, DARK_FLOPPY_THEME } from 'retro-floppy';

<FloppyDisk
  label={{ name: 'My App', author: 'Developer' }}
  theme={DARK_FLOPPY_THEME}
/>;

Neon Theme

Vibrant cyberpunk aesthetic with magenta and cyan accents.

import { FloppyDisk, NEON_THEME } from 'retro-floppy';

<FloppyDisk
  label={{ name: 'My App', author: 'Developer' }}
  theme={NEON_THEME}
/>;

Retro Theme

Classic 90s beige computer aesthetic with warm, vintage colors.

import { FloppyDisk, RETRO_THEME } from 'retro-floppy';

<FloppyDisk
  label={{ name: 'My App', author: 'Developer' }}
  theme={RETRO_THEME}
/>;

Pastel Theme

Soft, modern colors with gradient labels.

import { FloppyDisk, PASTEL_THEME } from 'retro-floppy';

<FloppyDisk
  label={{ name: 'My App', author: 'Developer' }}
  theme={PASTEL_THEME}
/>;

Custom Themes

Create your own theme by providing a custom theme object:

<FloppyDisk
  label={{ name: 'Custom', author: 'Me' }}
  theme={{
    diskColor: '#ff6b6b',
    slideColor: '#4ecdc4',
    backgroundColor: '#ffe66d',
    labelColor: '#ffffff',
    labelTextColor: '#2c3e50',
    enableGradient: false,
  }}
/>

Gradient Customization

When using gradients, you can customize the generation:

<FloppyDisk
  label={{ name: 'Gradient Demo', author: 'Dev' }}
  theme={{
    enableGradient: true,
    gradientType: 'linear', // 'linear', 'radial', 'conic', or 'auto'
    gradientOptions: {
      seed: 12345, // Custom seed for reproducible gradients
      colors: ['#ff6b6b', '#4ecdc4', '#45b7d1'], // Custom color palette
      angle: 135, // Angle for linear gradients (0-360)
    },
  }}
/>

API Reference

Props

| Prop | Type | Default | Description | | --------------- | -------------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------- | | size | 'tiny' \| 'small' \| 'medium' \| 'large' \| 'hero' \| number | 'medium' | Size of the disk. Predefined or custom px value | | label | FloppyLabel | undefined | Structured label data for the disk (name, author, year, description, type, size) | | diskType | 'HD' \| 'DD' | 'HD' | High Density or Double Density | | capacity | string | '1.44 MB' | Storage capacity display (overrides label.size if provided) | | theme | FloppyTheme | Default theme | Color customization object | | animation | AnimationConfig | {} | Animation timing and easing configuration | | variant | 'interactive' \| 'static' \| 'compact' | 'interactive' | Interaction mode | | selected | boolean | false | Whether the disk is selected | | disabled | boolean | false | Whether the disk is disabled | | loading | boolean | false | Shows subtle pulse animation | | error | boolean | false | Shows red label with white text | | onClick | () => void | - | Click handler | | onDoubleClick | () => void | - | Double-click handler | | onHover | (isHovered: boolean) => void | - | Hover state change handler | | onFocus | (isFocused: boolean) => void | - | Focus state change handler | | className | string | '' | Additional CSS class | | style | React.CSSProperties | - | Inline styles for root element | | data-testid | string | - | Test ID for testing libraries | | data-disk-id | string | - | Custom disk ID for tracking | | badge | React.ReactNode | - | Badge content (top-right corner) | | children | React.ReactNode | - | Custom overlay content | | ariaLabel | string | Auto-generated | Accessible label override |

Size Variants

| Size | Pixels | Use Case | | -------- | ---------- | ----------------------- | | tiny | 60px | Compact lists, icons | | small | 120px | Grid views, thumbnails | | medium | 200px | Featured items, cards | | large | 400px | Detail views, showcases | | hero | 600px | Landing pages, heroes | | Custom | Any number | Specific requirements |

Theme Object

interface FloppyTheme {
  diskColor?: string; // Main disk body color
  slideColor?: string; // Metal slide color
  backgroundColor?: string; // Background/cutout color
  labelColor?: string; // Label paper color
  labelTextColor?: string; // Label text color
  enableGradient?: boolean; // Enable dynamic gradient backgrounds (default: false)
  gradientType?: 'linear' | 'radial' | 'conic' | 'auto'; // Gradient type (default: 'auto')
  gradientOptions?: GradientOptions; // Custom gradient options
}

interface GradientOptions {
  seed?: number; // Custom seed for gradient generation
  colors?: string[]; // Custom color palette (HSL or hex colors)
  angle?: number; // Gradient angle for linear gradients (0-360 degrees)
}

Animation Config

interface AnimationConfig {
  hoverDuration?: number; // Hover animation duration in ms (default: 500)
  slideDuration?: number; // Slide animation duration in ms (default: 500)
  easing?: string; // Animation easing function (default: 'linear')
  disableAnimations?: boolean; // Disable all animations (default: false)
}

Variants

Interactive (Default)

  • Hover to slide out the metal shutter
  • Click to trigger the onClick handler
  • Double-click to trigger the onDoubleClick handler

Static

  • No hover animations
  • Click handlers still work
  • Best for performance-critical lists

Compact

  • Reduced label area
  • Smaller slide track
  • Optimized for tight spaces

Advanced Features

Loading and Error States

// Loading state with pulse animation
<FloppyDisk loading label={{ name: "Uploading..." }} />

// Error state with red label and white text
<FloppyDisk error label={{ name: "Failed" }} />

Badge and Overlays

// Simple badge
<FloppyDisk
  label={{ name: "New Release" }}
  badge={<span style={{ background: 'red', color: 'white', padding: '2px 6px', borderRadius: '4px' }}>NEW</span>}
/>

// Custom overlay
<FloppyDisk label={{ name: "Locked" }}>
  <div style={{ fontSize: '48px' }}>🔒</div>
</FloppyDisk>

Note: The lock emoji above is just an example. For production use, consider using an icon library or SVG instead.

Event Callbacks

<FloppyDisk
  label={{ name: 'Interactive' }}
  onHover={(isHovered) => console.log('Hover:', isHovered)}
  onFocus={(isFocused) => console.log('Focus:', isFocused)}
  onClick={() => console.log('Clicked')}
  onDoubleClick={() => console.log('Double-clicked')}
/>

Animation Customization

// Custom animation timing
<FloppyDisk
  animation={{
    hoverDuration: 1000,
    easing: 'ease-in-out'
  }}
/>

// Disable animations
<FloppyDisk animation={{ disableAnimations: true }} />

CSS Customization

// Using inline styles
<FloppyDisk
  style={
    {
      '--floppy-border-radius': '10%',
      '--floppy-hover-scale': '1.1',
      '--floppy-shadow-blur': '10px',
    } as React.CSSProperties
  }
/>;

// Using CSS module classes
import { floppyDiskStyles } from 'retro-floppy';

// Access individual classes for advanced styling
<div className={floppyDiskStyles.silhouette}>...</div>;

Available CSS Custom Properties

You can customize the appearance using CSS custom properties:

--floppy-size              /* Disk size in px */
--floppy-border            /* Border thickness */
--floppy-border-radius     /* Corner radius (default: 3%) */
--floppy-color             /* Main disk color */
--floppy-highlight         /* Highlight color */
--floppy-shadow            /* Shadow color */
--floppy-hover-scale       /* Hover scale factor (default: 1.02) */
--floppy-hover-brightness  /* Hover brightness (default: 1.05) */
--animation-duration       /* Animation duration (default: 500ms) */
--animation-easing         /* Animation easing (default: linear) */
--slide-color              /* Metal slide color */
--bg-color                 /* Background color */
--label-color              /* Label background */
--label-text-color         /* Label text color */
--label-text-shadow        /* Label text shadow */

Best Practices

Performance with Multiple Disks

When rendering many disks (50+), consider:

import { memo } from 'react';

const MemoizedFloppyDisk = memo(FloppyDisk);

// Use in your list
{
  disks.map((disk) => <MemoizedFloppyDisk key={disk.id} {...disk} />);
}

Responsive Sizing

// Use CSS custom properties for responsive sizing
<div style={{ '--floppy-size': 'clamp(80px, 15vw, 200px)' }}>
  <FloppyDisk size={120} />
</div>

Accessibility

<FloppyDisk
  label={{ name: 'Important Document' }}
  ariaLabel="Important Document floppy disk, double-click to open"
  onClick={handleSelect}
  onDoubleClick={handleOpen}
/>
  • The entire floppy is a focusable figure with role="button".
  • ariaLabel controls the screen reader label; if omitted, it falls back to the label text.
  • Keyboard users can press Enter or Space to trigger onClick when variant !== 'static' and disabled is false.
  • A visible focus outline appears when navigating with the keyboard.

Development

Running the Example

# Install dependencies
npm install

# Build the component
npm run build

# Run the example
cd example
npm install
npm run dev

Building for Production

npm run build

This creates:

  • dist/index.cjs - CommonJS bundle
  • dist/index.esm.js - ES Module bundle
  • dist/index.d.ts - TypeScript definitions
  • dist/retro-floppy.css - Extracted CSS styles for the component (import this in your app)

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

See CONTRIBUTING.md for development instructions and guidelines and CODE_OF_CONDUCT.md for expected behavior.

License

This project is licensed under the MIT License – see the LICENSE file for details.

Credits

Inspired by the iconic 3.5" floppy disk that stored our precious data in the 80s and 90s.


Made with ❤️ by Cameron Rye