JSPM

@punkbit/react-hn-reader

1.4.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 24
  • Score
    100M100P100Q56513F
  • License MIT

An experimental React component for displaying Hacker News stories with beautiful parallax scrolling

Package Exports

  • @punkbit/react-hn-reader

Readme

Hacker News Reader

An experimental Hacker News reader built with React, TypeScript, and modern tooling. Features parallax scrolling effects and a polished UX.

Hacker News Reader React TypeScript Vite

Overview

This project is a modern Hacker News reader that consumes data directly from the Hacker News Firebase REST API. It features:

  • ✨ A parallax-like scrolling inspired in starwars
  • ⚡ Lightning-fast development with Vite
  • 🎨 Customizable themes via styled-components
  • 📱 Responsive design
  • 🔧 Full TypeScript support

Installation

npm install react-hn-reader styled-components

Peer dependencies: react >=16.8 || >=17 || >=18 || >=19, react-dom >=16.8 || >=17 || >=18 || >=19, styled-components >=5.0 || >=6.0

Quick Start

Standard Usage (Full-Page Apps)

For apps where this component is the main content:

import { HackerNewsReader, ThemeProvider, DefaultStyles } from 'react-hn-reader'

function App() {
  return (
    <ThemeProvider>
      <DefaultStyles />
      <HackerNewsReader />
    </ThemeProvider>
  )
}

⚠️ Astro.js / iframe / Component Usage (IMPORTANT!)

Do NOT use DefaultStyles - it applies global CSS that breaks scoped styling. Use ScopedDefaultStyles instead:

import { 
  HackerNewsReader, 
  ThemeProvider, 
  ScopedDefaultStyles   // ✅ Use this, not DefaultStyles
} from 'react-hn-reader'

function App() {
  return (
    <ThemeProvider>
      {/* Wrap in div with data attribute for scoping */}
      <div data-react-hn-reader>
        <ScopedDefaultStyles />
        <HackerNewsReader 
          selfContained={true}  // ✅ Required for Astro/iframes
          scrollContainerHeight="100vh"
        />
      </div>
    </ThemeProvider>
  )
}

Why this matters:

  • DefaultStyles applies CSS resets to the entire page (body, html, *)
  • ScopedDefaultStyles only applies within [data-react-hn-reader] elements
  • selfContained={true} creates an internal scrollable container (required when parent has overflow: hidden)

Fonts are loaded automatically - Google Fonts (Bitter, Work Sans, Nanum Gothic) are injected dynamically when the component mounts.

Features

  • Parallax scrolling - 3D depth effect with three viewport layers
  • 🖱️ Scroll indicator - Animated mouse icon in top-right (fades on scroll)
  • 🖤 Fixed black logo - Appears at bottom-right when scrolling down
  • ➡️ Arrow icons - Each story displays an arrow-out icon
  • 🎨 Customizable themes - Override all colors via ThemeProvider
  • 🔧 TypeScript - Full type support
  • 📦 Astro.js Compatible - Works in scoped environments with selfContained mode

Props

Prop Type Default Description
apiUrl string HN Firebase API Base URL for stories API
initialCount number 30 Initial stories to load
enableLoadMore boolean true Show load more button
onStoryClick (story) => void - Click handler
theme object - Override theme colors
renderStory (story, index) => ReactNode - Custom story renderer
selfContained boolean false Enable internal scrolling container
scrollContainerHeight string | number '100vh' Height of scrollable area
containerClassName string - Additional CSS class for scroll container

Usage Examples

Custom Theme

<ThemeProvider theme={{ orange: '#ff4500', onyx: '#1a1a1a' }}>
  <DefaultStyles />
  <HackerNewsReader />
</ThemeProvider>

Custom API

<HackerNewsReader 
  apiUrl="https://your-api.com/v0"
  initialCount={50}
/>

Handle Clicks

<HackerNewsReader 
  onStoryClick={(story) => console.log(story.title)}
/>

🚀 Astro.js Integration

This component is specially designed to work with Astro.js scoped styles. Here's the complete setup:

1. Create a React wrapper component (e.g., src/components/HNReader.tsx):

import React from 'react'
import { 
  HackerNewsReader, 
  ThemeProvider, 
  ScopedDefaultStyles 
} from '@punkbit/react-hn-reader'

export function HNReader() {
  return (
    <ThemeProvider>
      <div data-react-hn-reader style={{ height: '100vh' }}>
        <ScopedDefaultStyles />
        <HackerNewsReader 
          selfContained={true}
          scrollContainerHeight="100vh"
        />
      </div>
    </ThemeProvider>
  )
}

2. Use in your Astro page:

---
import HNReader from '../components/HNReader';
---

<html>
  <head>
    <title>Hacker News Reader</title>
  </head>
  <body>
    <!-- This creates an isolated React island -->
    <HNReader client:load />
  </body>
</html>

⚠️ Important Notes for Astro.js:

  1. Always use ScopedDefaultStyles instead of DefaultStyles
  2. Always wrap in <div data-react-hn-reader> for CSS scoping
  3. Always set selfContained={true} because Astro adds scoped styles with overflow: hidden
  4. Set explicit height on the wrapper div (e.g., height: '100vh')

Why Astro.js needs special handling:

  • Astro applies scoped styles that add overflow: hidden to containers
  • selfContained={true} creates an internal scrollable container
  • ScopedDefaultStyles prevents CSS from leaking to other parts of your Astro page

📦 CDN Usage

For direct browser usage without npm:

<!DOCTYPE html>
<html>
<head>
  <title>HN Reader</title>
</head>
<body>
  <div id="root"></div>
  
  <script src="https://unpkg.com/@punkbit/react-hn-reader@latest/dist/cdn.js"></script>
  <script>
    const { React, ReactDOM, HackerNewsReader, ThemeProvider, ScopedDefaultStyles } = window.ReactHNReader;
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      React.createElement(ThemeProvider, null,
        React.createElement('div', { 'data-react-hn-reader': true, style: { height: '100vh' } },
          React.createElement(ScopedDefaultStyles, null),
          React.createElement(HackerNewsReader, { selfContained: true, scrollContainerHeight: '100vh' })
        )
      )
    );
  </script>
</body>
</html>

🔧 Self-Contained Mode Details

When selfContained={true}, the component:

  1. Creates an internal scrollable container with overflow-y: auto
  2. Changes position: fixed to position: relative/absolute internally
  3. Listens to container scroll events instead of window scroll
  4. Works inside iframes, modals, and containers with overflow: hidden

Use cases:

  • Astro.js: Scoped styles break window-based scrolling
  • iframes: Isolated scrolling context
  • Modals/Dialogs: Fixed containers without body scroll
  • Overflow hidden parents: Any ancestor with overflow: hidden

Self-Contained with Custom Height

// Fixed pixel height
<HackerNewsReader 
  selfContained={true} 
  scrollContainerHeight={800}
  containerClassName="my-scroll-container"
/>

// CSS string value
<HackerNewsReader 
  selfContained={true} 
  scrollContainerHeight="calc(100vh - 100px)"
/>

🎨 Styling Override Examples

Custom Scrollbar

/* Target the self-contained scroll container */
.my-scroll-container::-webkit-scrollbar {
  width: 12px;
}

.my-scroll-container::-webkit-scrollbar-thumb {
  background: #ff6600;
  border-radius: 6px;
}

Override Position in Self-Contained Mode

<HackerNewsReader 
  selfContained={true}
  containerClassName="hn-reader-wrapper"
/>
.hn-reader-wrapper {
  border: 2px solid #ff6600;
  border-radius: 8px;
}

Fonts

Google Fonts are loaded automatically when the component mounts:

  • Bitter - Primary text font
  • Work Sans - Headings
  • Nanum Gothic - Author names

No manual configuration needed!

Development

npm install
npm run build    # Build package (ESM)
npm run build:cdn  # Build CDN version (IIFE)
npm run dev      # Development mode with example

Local Testing with Preview Build

Build a standalone preview version that bundles React and all dependencies:

npm run build:preview    # Build standalone preview to dist/preview/
npm run serve            # Serve with http-server on http://localhost:8080

Or manually:

npm run build:preview
npx http-server ./dist/preview -p 8080 -o

The preview build is a fully self-contained version that doesn't require external React dependencies, making it perfect for testing the component in isolation before publishing.

Build Everything

To build all versions (preview, library, and CDN):

npm run build:all   # Builds preview, library, and CDN

Troubleshooting

"Can't scroll" in Astro.js

Problem: Astro's scoped styles add overflow: hidden that prevents scrolling.

Solution: Use selfContained={true} + ScopedDefaultStyles:

<div data-react-hn-reader style={{ height: '100vh' }}>
  <ScopedDefaultStyles />
  <HackerNewsReader selfContained={true} scrollContainerHeight="100vh" />
</div>

"CSS leaking to other parts of page"

Problem: DefaultStyles applies global CSS resets.

Solution: Use ScopedDefaultStyles with data-react-hn-reader wrapper.

"Parallax effect not working in iframe"

Problem: position: fixed elements don't work inside scrollable containers.

Solution: Enable selfContained={true} - it automatically switches to position: relative/absolute.

Publish

npm run build
npm publish

License

MIT © Punkbit