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

@switch-to-eu/layout-ui

v0.2.1

Published

Modular UI design system for privacy-focused tools - React components with Tailwind CSS and theme customization

Readme

@switch-to-eu/layout-ui

npm version License: MIT TypeScript

A modular, themeable UI design system for privacy-focused tools. Built with React, TypeScript, Tailwind CSS, and Radix UI primitives for maximum accessibility and customization.

✨ Features

  • 🎨 Fully Themeable: Customize colors via CSS custom properties
  • 📱 Responsive Design: Mobile-first approach with Tailwind CSS
  • Accessibility First: Built on Radix UI primitives with WCAG compliance
  • 🔧 TypeScript Native: 100% TypeScript with full type safety
  • 📚 Storybook Included: Interactive component documentation
  • 🎯 Tree Shakeable: Import only the components you need (~32KB full bundle)
  • 🔄 Form Integration: React Hook Form + Zod validation support
  • 🌙 Dark Mode Ready: Built-in light/dark theme switching
  • Modern Stack: React 19, Next.js 15, latest tooling

📦 Installation

npm install @switch-to-eu/layout-ui

Peer Dependencies

Ensure you have the required peer dependencies:

npm install react@^19.0.0 react-dom@^19.0.0 next@^15.2.3

🚀 Quick Start

1. Import Styles

Import the base styles in your project (usually in globals.css or layout.tsx):

/* Option 1: Import main styles (includes globals.css) */
@import "@switch-to-eu/layout-ui/styles";

/* Option 2: Import specific style files */
@import "@switch-to-eu/layout-ui/styles/globals.css";
@import "@switch-to-eu/layout-ui/styles/themes.css";

Or in your TypeScript/JavaScript files:

// In your root layout or App component
import "@switch-to-eu/layout-ui/styles/globals.css";

2. Use Components

import { Button, Card, CardHeader, CardTitle, CardContent } from "@switch-to-eu/layout-ui";

function App() {
  return (
    <Card className="w-96">
      <CardHeader>
        <CardTitle>Welcome to Layout UI</CardTitle>
      </CardHeader>
      <CardContent>
        <Button variant="default" size="lg">
          Get Started
        </Button>
      </CardContent>
    </Card>
  );
}

3. Customize Theme (Optional)

Override CSS custom properties to match your brand:

/* In your globals.css */
:root {
  --primary: 262 83% 58%;        /* Purple */
  --secondary: 220 14% 96%;      /* Light gray */
  --accent: 142 76% 36%;         /* Green */
  --destructive: 0 72% 51%;      /* Red */
  --radius: 0.75rem;             /* Border radius */
}

🧩 Available Components

Core UI Components

| Component | Description | Features | |-----------|-------------|----------| | Button | Interactive buttons with variants | 6 variants, loading states, icons | | Card | Content containers | Header, content, footer composition | | Input | Text input fields | Validation states, descriptions | | Label | Form labels | Accessibility optimized | | Alert | Status messages | Success, warning, error, info variants | | Skeleton | Loading placeholders | Animated, responsive | | Checkbox | Boolean input | Radix UI integration | | Select | Dropdown selection | Radix UI integration | | Dialog | Modal dialogs | Radix UI integration | | Calendar | Date picker | date-fns integration |

Composite Components

| Component | Description | Use Case | |-----------|-------------|----------| | SectionCard | Structured content sections | Main content areas | | SectionHeader | Section headings with icons | Page/section titles | | Header | Navigation headers | App navigation | | LoadingButton | Button with loading state | Form submissions |

Form Components

| Component | Description | Integration | |-----------|-------------|-------------| | FormInput | React Hook Form input | Full validation, error handling | | FormTextarea | React Hook Form textarea | Multi-line text input | | Textarea | Base textarea component | Standalone usage |

🎨 Theme Customization

Method 1: CSS Custom Properties (Recommended)

Override theme variables in your CSS:

/* Your project's globals.css */
@import "@switch-to-eu/layout-ui/styles";

/* Light theme customization */
:root {
  /* Brand colors */
  --primary: 262 83% 58%;           /* Purple */
  --primary-foreground: 0 0% 100%;  /* White text */

  /* Secondary colors */
  --secondary: 220 14% 96%;         /* Light gray */
  --secondary-foreground: 222 84% 5%; /* Dark text */

  /* Accent colors */
  --accent: 142 76% 36%;            /* Green */
  --accent-foreground: 0 0% 100%;   /* White text */

  /* Status colors */
  --destructive: 0 72% 51%;         /* Red */
  --destructive-foreground: 0 0% 100%; /* White text */

  /* Layout */
  --radius: 0.75rem;                /* Border radius */
  --border: 220 13% 91%;            /* Border color */
}

/* Dark theme customization */
.dark {
  --primary: 262 83% 70%;           /* Lighter purple for dark mode */
  --secondary: 217 32% 17%;         /* Dark gray */
  --background: 222 84% 5%;         /* Dark background */
  --foreground: 210 40% 98%;        /* Light text */
}

Method 2: JavaScript Theme Utilities

Use the built-in theme utilities for dynamic theming:

import { applyTheme, setColorMode, getSystemColorMode } from "@switch-to-eu/layout-ui";

// Apply custom theme programmatically
applyTheme({
  primary: "262 83% 58%",
  accent: "142 76% 36%",
  destructive: "0 72% 51%"
});

// Handle dark mode
setColorMode("dark");
setColorMode("light");
setColorMode("system"); // Follow system preference

// Get current system preference
const systemMode = getSystemColorMode(); // "light" | "dark"

Complete Theme Properties Reference

/* Color Properties (HSL values without hsl()) */
--background: /* Main background */
--foreground: /* Main text color */
--primary: /* Primary brand color */
--primary-foreground: /* Text on primary */
--secondary: /* Secondary color */
--secondary-foreground: /* Text on secondary */
--accent: /* Accent color */
--accent-foreground: /* Text on accent */
--destructive: /* Error/danger color */
--destructive-foreground: /* Text on destructive */
--muted: /* Muted backgrounds */
--muted-foreground: /* Muted text */
--card: /* Card backgrounds */
--card-foreground: /* Text on cards */
--border: /* Border color */
--input: /* Input border color */
--ring: /* Focus ring color */

/* Layout Properties */
--radius: /* Border radius (in rem) */

🔧 Form Integration

React Hook Form + Zod Example

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { FormInput, FormTextarea, Button, Card } from "@switch-to-eu/layout-ui";

const schema = z.object({
  name: z.string().min(2, "Name must be at least 2 characters"),
  email: z.string().email("Invalid email address"),
  message: z.string().min(10, "Message must be at least 10 characters")
});

type FormData = z.infer<typeof schema>;

function ContactForm() {
  const { control, handleSubmit, formState: { isSubmitting } } = useForm<FormData>({
    resolver: zodResolver(schema)
  });

  const onSubmit = async (data: FormData) => {
    // Handle form submission
    console.log(data);
  };

  return (
    <Card className="p-6">
      <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
        <FormInput
          control={control}
          name="name"
          label="Full Name"
          placeholder="Enter your name"
        />
        <FormInput
          control={control}
          name="email"
          label="Email Address"
          type="email"
          placeholder="Enter your email"
        />
        <FormTextarea
          control={control}
          name="message"
          label="Message"
          placeholder="Enter your message"
          rows={4}
        />
        <Button type="submit" loading={isSubmitting}>
          {isSubmitting ? "Sending..." : "Send Message"}
        </Button>
      </form>
    </Card>
  );
}

📖 Development

Setup

# Clone the repository
git clone https://github.com/switch-to-eu/layout-ui.git
cd layout-ui

# Install dependencies
npm install

# Start development mode with hot reload
npm run dev

# Start Storybook for component development
npm run storybook

Available Scripts

| Script | Description | |--------|-------------| | npm run build | Build the library for production | | npm run dev | Start development mode with watch | | npm run storybook | Start Storybook development server | | npm run build-storybook | Build Storybook for production | | npm run lint | Run ESLint | | npm run type-check | Run TypeScript compiler check | | npm run publish:dry | Test publish without actually publishing | | npm run publish:beta | Publish beta version to npm |

Project Structure

layout-ui/
├── src/
│   ├── components/
│   │   ├── ui/              # Core UI components
│   │   │   ├── Button.tsx
│   │   │   ├── Card.tsx
│   │   │   ├── Input.tsx
│   │   │   └── ...
│   │   ├── form/            # Form components
│   │   │   ├── FormInput.tsx
│   │   │   ├── FormTextarea.tsx
│   │   │   └── FormUtils.tsx
│   │   └── index.ts         # Component exports
│   ├── lib/
│   │   ├── utils.ts         # Utility functions
│   │   └── theme.ts         # Theme management
│   ├── styles/
│   │   ├── globals.css      # Base styles
│   │   └── themes.css       # Theme system
│   ├── types/
│   │   └── index.ts         # TypeScript definitions
│   └── index.ts             # Main library export
├── stories/                 # Storybook stories
├── .storybook/             # Storybook configuration
├── dist/                   # Built library output
└── docs/                   # Documentation

📋 Publishing

This library is published to npm as @switch-to-eu/layout-ui.

Version Management

# Patch version (bug fixes)
npm run version:patch

# Minor version (new features)
npm run version:minor

# Major version (breaking changes)
npm run version:major

Publishing Process

# 1. Test the build
npm run build
npm run type-check

# 2. Test publish without actually publishing
npm run publish:dry

# 3. Publish to npm
npm publish

# Or publish beta version
npm run publish:beta

🤝 Contributing

This library is part of the privacy-tools ecosystem. Components are extracted and made generic from existing projects to maintain consistency.

Guidelines

  1. Keep it generic - No project-specific code in the library
  2. Theme-first - All styling should use CSS custom properties
  3. Accessibility - Use Radix UI primitives where possible
  4. TypeScript - Full type safety required
  5. Documentation - Include Storybook stories for all components

Component Migration Process

  1. Extract component from existing project
  2. Remove project-specific styling/logic
  3. Convert to use theme variables
  4. Add proper TypeScript interfaces
  5. Create Storybook story
  6. Update exports and documentation

📄 License

MIT License - see LICENSE file for details.

🔗 Links

🙏 Acknowledgments


Built with ❤️ for privacy-focused applications