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

@page-speed/pressable

v0.0.7

Published

Performance-optimized universal link/button component with automatic URL detection and normalization for DashTrack ecosystem

Readme

@page-speed/pressable

Performance-optimized universal link/button component with automatic URL detection and normalization for the OpenSite Semantic Site Builder ecosystem. Provides tree-shakable, performance-optimized components with abstract styling support

Page Speed Pressable Component

npm version npm downloads TypeScript

Features

  • 🔗 Universal Component: Automatically renders <a>, <button>, or fallback elements based on props
  • 🌐 Smart URL Detection: Automatically detects and normalizes internal, external, mailto, and tel links
  • 📱 Phone Number Normalization: Converts various phone formats to standard tel: format
  • ✉️ Email Normalization: Automatically adds mailto: prefix to email addresses
  • 🎨 ShadCN Button Variants: Full integration with ShadCN button styles and variants
  • Accessibility First: Proper ARIA attributes, keyboard navigation, and screen reader support
  • 🎯 SEO Optimized: Internal links always render as <a> tags for proper SEO
  • 🌲 Tree-Shakable: Granular exports for minimal bundle size
  • 🚀 Zero Runtime Overhead: Efficient memoization and minimal re-renders
  • 🔒 Type Safe: Full TypeScript support with comprehensive types

Installation

```bash

Using pnpm (recommended)

pnpm add @page-speed/pressable

Using npm

npm install @page-speed/pressable

Using yarn

yarn add @page-speed/pressable ```

Peer Dependencies

```json { "react": ">=17.0.0", "react-dom": ">=17.0.0" } ```

Setup Requirements

1. Tailwind CSS Configuration

CRITICAL: Add @page-speed/pressable to your Tailwind content paths so button styles are included:

```ts // tailwind.config.ts import type { Config } from "tailwindcss";

const config: Config = { content: [ "./app//*.{js,ts,jsx,tsx,mdx}", "./components//*.{js,ts,jsx,tsx,mdx}", // Add one of these lines to scan Pressable's button-variant classes:

// For standard npm/yarn installations:
"./node_modules/@page-speed/pressable/dist/**/*.{js,cjs}",

// For pnpm monorepos (use both if unsure):
"./node_modules/.pnpm/@page-speed+pressable*/node_modules/@page-speed/pressable/**/*.{js,jsx,ts,tsx}",

], // ...rest of config }; ```

Without this, button variants won't have styles applied because Tailwind will purge the classes.

2. Router Setup (For Navigation)

Wrap your app with `RouterProvider` from `@page-speed/router` to enable internal navigation.

For Next.js App Router (requires client component wrapper):

```tsx // components/providers/RouterWrapper.tsx "use client";

import { RouterProvider } from "@page-speed/router"; import { ReactNode } from "react";

export function RouterWrapper({ children }: { children: ReactNode }) { return {children}; } ```

```tsx // app/layout.tsx import { RouterWrapper } from "@/components/providers/RouterWrapper";

export default function RootLayout({ children }) { return ( {children} ); } ```

For standard React apps (Create React App, Vite, etc.):

```tsx // App.tsx import { RouterProvider } from "@page-speed/router";

function App() { return ( {/* your app */} ); } ```

Install `@page-speed/router` directly for better type support:

```bash pnpm add @page-speed/router ```

Basic Usage

Simple Link

```tsx import { Pressable } from "@page-speed/pressable";

function Navigation() { return About Us; } ```

External Link

Automatically gets `target="_blank"` and `rel="noopener noreferrer"`:

```tsx Visit Google ```

Button-Styled Link

```tsx Contact Us ```

Phone Link

Automatically normalized to `tel:` format:

```tsx Call Us // Renders: Call Us ```

Email Link

Automatically normalized to `mailto:` format:

```tsx Email Us // Renders: Email Us ```

Button with onClick

```tsx <Pressable onClick={() => alert("Clicked")} asButton variant="default"> Click Me ```

Advanced Usage

Button Variants

Supports all ShadCN button variants:

```tsx // Default variant Primary

// Outline variant Outline

// Secondary variant Secondary

// Ghost variant Ghost

// Link variant Link Style

// Destructive variant Delete ```

Button Sizes

```tsx Small Default Medium Large

// Icon sizes ```

Custom Layouts

Full control over children:

```tsx

Accessibility

```tsx <Pressable href="/important" aria-label="Important action" aria-describedby="description" id="important-link"

Click here for important information ```

Refs

```tsx const linkRef = useRef(null);

API Reference

Props

Core Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | `children` | `ReactNode` | - | Content inside the component | | `href` | `string` | - | URL to navigate to (supports internal, external, mailto, tel) | | `onClick` | `MouseEventHandler` | - | Click handler function | | `className` | `string` | - | Additional CSS classes | | `asButton` | `boolean` | `false` | Apply button styles even when rendering as `` |

Button Styling

| Prop | Type | Default | Description | |------|------|---------|-------------| | `variant` | `'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'` | - | Button variant style | | `size` | `'default' | 'sm' | 'md' | 'lg' | 'icon' | 'icon-sm' | 'icon-lg'` | - | Button size |

Component Type

| Prop | Type | Default | Description | |------|------|---------|-------------| | `componentType` | `'a' | 'button' | 'span' | 'div'` | auto | Explicit component type to render | | `fallbackComponentType` | `'span' | 'div' | 'button'` | `'span'` | Component to render when no href/onClick |

Accessibility

| Prop | Type | Default | Description | |------|------|---------|-------------| | `aria-label` | `string` | - | ARIA label for accessibility | | `aria-describedby` | `string` | - | ARIA describedby reference | | `id` | `string` | - | Element ID |

Data Attributes

Any `data-*` attributes are automatically forwarded to the rendered element.

URL Detection & Normalization

Internal Links

Full URLs matching the current origin are automatically converted to relative paths:

```tsx // On https://example.com About // Renders: About ```

Phone Number Formats

Supports various phone number formats:

```tsx // → tel:+14322386131 // → tel:+5122322212 // → tel:+5122322212 // → tel:+14322386131 // → tel:+5122322212;ext=123 ```

Email Detection

Automatically detects email addresses:

```tsx // → mailto:[email protected] // → mailto:[email protected] (unchanged) ```

Hooks

useNavigation

Low-level hook for custom navigation logic:

```tsx import { useNavigation } from "@page-speed/pressable/hooks";

function CustomLink({ href }) { const { linkType, normalizedHref, target, rel, isInternal, isExternal, handleClick, } = useNavigation({ href });

return ( {href} ); } ```

useNavigation Return Values

| Property | Type | Description | |----------|------|-------------| | `linkType` | `'internal' | 'external' | 'mailto' | 'tel' | 'none' | 'unknown'` | Detected link type | | `normalizedHref` | `string | undefined` | Normalized URL | | `target` | `'_blank' | '_self' | undefined` | Link target attribute | | `rel` | `string | undefined` | Link rel attribute | | `isInternal` | `boolean` | Whether link is internal | | `isExternal` | `boolean` | Whether link is external | | `shouldUseRouter` | `boolean` | Whether to use client-side routing | | `handleClick` | `MouseEventHandler` | Click handler function |

Utilities

cn

Utility for merging Tailwind classes:

```tsx import { cn } from "@page-speed/pressable/utils";

function CustomButton() { return ( <Pressable href="/test" className={cn( "base-class", isActive && "active-class", { "conditional": someCondition } )} > Custom Button ); } ```

Integration with opensite-blocks

The Pressable component integrates seamlessly with the opensite-blocks navigation system:

```tsx // Set up navigation handler (typically done in opensite-blocks) window.__opensiteNavigationHandler = (href, event) => { // Custom navigation logic (e.g., React Router) navigate(href); return true; // Indicates navigation was handled };

// Pressable automatically uses the handler for internal links About ```

CSS Variables

The component supports extensive CSS variable customization for button styles. See the button-variants.ts file for the complete list of CSS variables.

Master Variables

```css :root { --button-font-family: inherit; --button-font-weight: 500; --button-letter-spacing: 0; --button-line-height: 1.25; --button-text-transform: none; --button-transition: all 250ms cubic-bezier(0.4, 0, 0.2, 1); --button-radius: 0.375rem; --button-shadow: none; --button-shadow-hover: none; } ```

Per-Variant Variables

```css :root { /* Default variant */ --button-default-bg: hsl(var(--primary)); --button-default-fg: hsl(var(--primary-foreground)); --button-default-hover-bg: hsl(var(--primary) / 0.9);

/* Outline variant */ --button-outline-bg: hsl(var(--background)); --button-outline-border: hsl(var(--border)); --button-outline-border-width: 1px;

/* ... and more */ } ```

Tree-Shaking

The package is fully tree-shakable. Import only what you need:

```tsx // Import specific components import { Pressable } from "@page-speed/pressable/core"; import { useNavigation } from "@page-speed/pressable/hooks"; import { cn } from "@page-speed/pressable/utils";

// Or use granular imports import { Pressable } from "@page-speed/pressable/core/Pressable"; import { buttonVariants } from "@page-speed/pressable/core/button-variants"; ```

Performance

  • Bundle Size: ~8KB gzipped (including all dependencies)
  • Tree-Shaking: Unused code is automatically eliminated
  • Memoization: All computed values are memoized with React.useMemo
  • Zero Runtime Overhead: Efficient URL detection and normalization
  • SSR Compatible: Works seamlessly with server-side rendering

Browser Support

  • Modern browsers (Chrome, Firefox, Safari, Edge)
  • React 17+
  • Server-side rendering (SSR)
  • Static site generation (SSG)

License

MIT

Contributing

Contributions are welcome! Please follow the DashTrack ecosystem guidelines.

Related Packages

Support