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

@newtonedev/components

v0.1.12

Published

React + React Native Web component library for Newtone

Readme

@newtone/components

Production-ready React component library built on the Newtone color engine. Cross-platform support for web and React Native using react-native-web.

Features

  • 🎨 Powered by Newtone — Accessible semantic color systems with OKLCH color space
  • 🌓 Light/Dark Mode — Automatic theme switching with Context API
  • 🎯 Four Themes — Neutral, Primary, Secondary, Strong with elevation support
  • 📱 Cross-Platform — Single codebase for web and React Native
  • 🔧 Runtime Tokens — Dynamic color computation with memoization
  • ♿️ Accessible — WCAG contrast ratios built-in
  • 📦 Zero Config — Sensible defaults, fully customizable
  • 🎭 TypeScript — Full type safety

Installation

npm install @newtone/components react react-native

Additional Setup

For Web Projects:

npm install react-native-web react-dom

For React Native Projects: No additional dependencies required.

Quick Start

Web Application

import React from 'react';
import ReactDOM from 'react-dom/client';
import { NewtoneProvider, Button } from '@newtone/components';

function App() {
  return (
    <NewtoneProvider initialMode="light" initialTheme="neutral">
      <div style={{ padding: 20 }}>
        <Button variant="primary" onPress={() => console.log('Clicked!')}>
          Click me
        </Button>
      </div>
    </NewtoneProvider>
  );
}

ReactDOM.createRoot(document.getElementById('root')!).render(<App />);

React Native Application

import React from 'react';
import { SafeAreaView } from 'react-native';
import { NewtoneProvider, Button } from '@newtone/components';

export default function App() {
  return (
    <NewtoneProvider initialMode="light" initialTheme="neutral">
      <SafeAreaView style={{ flex: 1, padding: 20 }}>
        <Button variant="primary" onPress={() => console.log('Clicked!')}>
          Click me
        </Button>
      </SafeAreaView>
    </NewtoneProvider>
  );
}

Components

Button

A cross-platform button component with multiple variants and sizes.

Props

interface ButtonProps {
  children: React.ReactNode;
  variant?: 'primary' | 'secondary' | 'ghost' | 'outline'; // default: 'primary'
  size?: 'sm' | 'md' | 'lg'; // default: 'md'
  disabled?: boolean; // default: false
  onPress?: () => void;
  style?: ViewStyle | ViewStyle[];
  textStyle?: TextStyle | TextStyle[];
  // ... all other Pressable props
}

Variants

  • Primary — Filled button with interactive color background
  • Secondary — Filled button with elevated background
  • Ghost — Transparent button with colored text
  • Outline — Border-only button with colored text

Sizes

  • Small (sm) — Compact button for tight spaces (6px vertical padding)
  • Medium (md) — Default size for most use cases (10px vertical padding)
  • Large (lg) — Prominent button for primary actions (14px vertical padding)

Examples

import { Button } from '@newtone/components';

// Primary button (default)
<Button onPress={() => {}}>Submit</Button>

// Secondary button
<Button variant="secondary" onPress={() => {}}>Cancel</Button>

// Large ghost button
<Button variant="ghost" size="lg" onPress={() => {}}>Learn More</Button>

// Disabled outline button
<Button variant="outline" disabled>Unavailable</Button>

// Custom styles
<Button
  variant="primary"
  style={{ marginTop: 20, width: 200 }}
  textStyle={{ fontWeight: 'bold' }}
  onPress={() => {}}
>
  Custom Styled
</Button>

Theme System

NewtoneProvider

Wrap your application root with NewtoneProvider to enable theming.

import { NewtoneProvider } from '@newtone/components';

<NewtoneProvider
  config={customConfig} // optional, uses defaults if omitted
  initialMode="light" // 'light' | 'dark', default: 'light'
  initialTheme="neutral" // 'neutral' | 'primary' | 'secondary' | 'strong', default: 'neutral'
>
  <App />
</NewtoneProvider>

Default Configuration

The library ships with a sensible default configuration:

  • 5 Palettes: Neutral (gray), Accent (blue), Success (green), Warning (yellow), Error (red)
  • 4 Themes: Each maps to a palette with light/dark normalized values
  • 3 Elevations: Surface levels with subtle color shifts (-0.02, 0, +0.04)
  • Dynamic Range: Lightest = 1 (white), Darkest = 1 (black)

useNewtoneTheme Hook

Access and modify theme state from any component within the provider.

import { useNewtoneTheme } from '@newtone/components';

function ThemeToggle() {
  const { mode, theme, setMode, setTheme } = useNewtoneTheme();

  return (
    <div>
      <button onClick={() => setMode(mode === 'light' ? 'dark' : 'light')}>
        Toggle {mode} mode
      </button>
      <button onClick={() => setTheme('primary')}>
        Switch to primary theme
      </button>
    </div>
  );
}

useTokens Hook

Compute design tokens for the current theme context at a specific elevation.

import { useTokens } from '@newtone/components';

function CustomComponent() {
  const tokens = useTokens(1); // Elevation level: 0, 1, or 2

  return (
    <div style={{
      backgroundColor: tokens.background.srgb,
      color: tokens.textPrimary.srgb
    }}>
      Custom component using tokens
    </div>
  );
}

Available Tokens

interface ResolvedTokens {
  background: ColorResult;           // Base surface color
  backgroundElevated: ColorResult;   // Elevated surface (+1 level)
  backgroundSunken: ColorResult;     // Sunken surface (-1 level)
  textPrimary: ColorResult;          // Primary text (WCAG 4.5:1)
  textSecondary: ColorResult;        // Secondary text (WCAG 3.0:1)
  interactive: ColorResult;          // Interactive elements (accent palette)
  interactiveHover: ColorResult;     // Hover state
  interactiveActive: ColorResult;    // Active/pressed state
  border: ColorResult;               // Subtle borders
}

Each ColorResult contains:

{
  srgb: { r: number, g: number, b: number }, // 0-1 range
  oklch: { L: number, C: number, h: number }
}

Helper for hex conversion:

import { srgbToHex } from 'newtone';

const hexColor = srgbToHex(tokens.background.srgb); // "#ffffff"

Customization

Custom Color System

Override the default palette configuration:

import { NewtoneProvider } from '@newtone/components';
import type { ColorSystemConfig } from 'newtone';

const customColorSystem: ColorSystemConfig = {
  dynamicRange: {
    lightest: 1,    // 0-1, where 1 = pure white
    darkest: 1,     // 0-1, where 1 = pure black
  },
  palettes: [
    {
      hue: 220,           // OKLCH hue (0-360)
      saturation: 80,     // 0-100, relative to max in-gamut chroma
      desaturation: {
        direction: 'light',  // 'light' | 'dark'
        strength: 'medium'   // 'low' | 'medium' | 'hard'
      },
      paletteHueGrading: {
        hue: 200,
        strength: 'low',
        direction: 'light'
      }
    },
    // ... more palettes
  ],
};

const customConfig = {
  colorSystem: customColorSystem,
  themes: {
    neutral: { paletteIndex: 0, lightModeNv: 0.97, darkModeNv: 0.08 },
    primary: { paletteIndex: 1, lightModeNv: 0.50, darkModeNv: 0.45 },
    secondary: { paletteIndex: 2, lightModeNv: 0.60, darkModeNv: 0.55 },
    strong: { paletteIndex: 3, lightModeNv: 0.30, darkModeNv: 0.70 },
  },
  elevation: {
    offsets: [-0.02, 0, 0.04] as const,
  },
};

<NewtoneProvider config={customConfig}>
  <App />
</NewtoneProvider>

Custom Button Styles

Override button appearance with style and textStyle props:

<Button
  variant="primary"
  style={[
    { borderRadius: 20, paddingHorizontal: 30 },
    isHighlighted && { transform: [{ scale: 1.05 }] }
  ]}
  textStyle={{
    fontFamily: 'CustomFont',
    letterSpacing: 1
  }}
>
  Custom Button
</Button>

Architecture

Token Computation Strategy

Phase 1 (Current): Runtime Computation

Tokens are computed on-demand using the Newtone color engine:

Config → getColor() → OKLCH → Gamut Mapping → sRGB → ColorResult
  • ✅ Maximum flexibility (change config, colors update instantly)
  • ✅ Smallest bundle (no pre-generated tokens)
  • ✅ Always accurate (no stale tokens)
  • ⚠️ Requires engine in bundle (~20KB minified)
  • ⚠️ Runtime computation cost (mitigated by memoization)

Phase 2 (Future): Hybrid Strategy

  • Development: Runtime computation (hot reload, instant feedback)
  • Production: Build-time token generation (W3C Design Tokens JSON)
  • Zero engine in production bundle
  • Cross-platform export (CSS, iOS, Android, etc.)

Component Patterns

All components follow these conventions:

  1. Pure Components — Props in, UI out, no side effects
  2. Token-Driven — Colors from useTokens(), not hardcoded
  3. Cross-PlatformPressable, Text, StyleSheet from React Native
  4. Memoized StylesuseMemo for style computation
  5. Accessible — WCAG-compliant contrast ratios
  6. Type-Safe — Full TypeScript with readonly interfaces

File Structure

packages/components/
├── src/
│   ├── theme/
│   │   ├── types.ts              # ColorMode, ThemeName, ThemeMapping
│   │   ├── defaults.ts           # DEFAULT_THEME_CONFIG
│   │   └── NewtoneProvider.tsx   # Context provider + hook
│   ├── tokens/
│   │   ├── types.ts              # ResolvedTokens interface
│   │   ├── computeTokens.ts      # Pure token computation
│   │   └── useTokens.ts          # Memoized token hook
│   ├── Button/
│   │   ├── Button.tsx            # Component implementation
│   │   ├── Button.types.ts       # Props and type definitions
│   │   ├── Button.styles.ts      # Style computation function
│   │   └── index.ts              # Barrel export
│   └── index.ts                  # Public API
├── tests/
│   ├── theme.test.tsx            # Theme system tests
│   ├── tokens.test.ts            # Token computation tests
│   └── Button.test.tsx           # Button component tests
└── package.json

Platform-Specific Notes

Web

  • Uses react-native-web to render React Native primitives as HTML
  • Pressable<button> or <div> with click handlers
  • Text<span>
  • StyleSheet → Optimized CSS-in-JS

Bundler Configuration:

Most modern bundlers (Vite, Next.js, Create React App) automatically handle react-native-web aliasing. If needed, manually alias:

// vite.config.js
export default {
  resolve: {
    alias: {
      'react-native': 'react-native-web',
    },
  },
};

React Native

  • Native components render directly
  • No additional bundler configuration needed
  • Full gesture support via Pressable
  • Metro bundler resolves react-native imports automatically

Testing

The library uses Vitest + React Testing Library for comprehensive testing:

cd packages/components
npm test              # Run tests in watch mode
npm run test:run      # Run tests once
npm run test:coverage # Generate coverage report

Test Setup:

  • Environment: jsdom (browser simulation)
  • Alias: react-nativereact-native-web for web compatibility
  • Coverage: 90% threshold (branches, functions, lines, statements)

Example Test:

import { render, fireEvent } from '@testing-library/react';
import { Button, NewtoneProvider } from '@newtone/components';

it('calls onPress when clicked', () => {
  const onPress = vi.fn();
  const { getByText } = render(
    <NewtoneProvider>
      <Button onPress={onPress}>Click me</Button>
    </NewtoneProvider>
  );

  fireEvent.click(getByText('Click me'));
  expect(onPress).toHaveBeenCalledTimes(1);
});

TypeScript

All types are exported for consumer applications:

import type {
  // Components
  ButtonProps,
  ButtonVariant,
  ButtonSize,

  // Theme System
  ColorMode,
  ThemeName,
  ElevationLevel,
  ThemeMapping,
  ColorSystemConfig,
  NewtoneThemeConfig,
  NewtoneThemeContext,

  // Tokens
  ResolvedTokens,

  // Engine (re-exported from newtone)
  DynamicRange,
  PaletteConfig,
  ColorResult,
  Srgb,
  Oklch,
  HexColor,
} from '@newtone/components';

Roadmap

Phase 1 (Complete) ✅

  • [x] Monorepo setup with npm workspaces
  • [x] Theme system (NewtoneProvider, useNewtoneTheme)
  • [x] Runtime token computation (useTokens)
  • [x] Button component (4 variants, 3 sizes)
  • [x] Comprehensive test suite (48 tests)
  • [x] Documentation

Phase 2 (Planned)

  • [ ] Build-time token generation (W3C Design Tokens JSON)
  • [ ] CSS variable export for web
  • [ ] Platform-specific token formats (iOS, Android)
  • [ ] Additional components (Input, Card, Modal, etc.)
  • [ ] Storybook integration
  • [ ] Visual regression testing
  • [ ] Performance benchmarks

Contributing

This library is part of the Newtone project. Please refer to the main repository for contribution guidelines.

License

MIT

Support

For issues, questions, or feature requests, please open an issue in the main Newtone repository.


Built with ❤️ by the Newtone team