
Research
Shai-Hulud Descends to Hades: Miasma Worm Campaign Spreads with New PyPI Wave
Socket found 37 malicious PyPI wheels that abuse Python startup hooks to launch a Bun-powered credential stealer tied to Mini Shai-Hulud/Miasma.
@opensite/ui
Advanced tools
Foundational UI component library for OpenSite Semantic Site Builder with tree-shakable exports and abstract styling

Below you can see the wide range of layouts that are available automatically, enabling a modern UI experience for users. And since the component was specifically engineered for our Semantic UI engine, in addition to the default layout and style variants.
pnpm add @opensite/ui
# or
npm install @opensite/ui
This library requires React 16.8.0 or higher:
pnpm add react react-dom
CRITICAL: Add both @opensite/ui and @page-speed/pressable to your Tailwind content paths:
// 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 these lines to scan component classes:
"./node_modules/@opensite/ui/dist/**/*.{js,mjs}",
"./node_modules/@page-speed/pressable/dist/**/*.{js,mjs}",
],
// ...rest of config
};
Without this, button styles from Pressable and other components won't be applied.
The Pressable component (used for links/buttons) requires RouterProvider from @page-speed/router.
For Next.js App Router (requires client component wrapper):
// components/providers/RouterWrapper.tsx
"use client";
import { RouterProvider } from "@page-speed/router";
import { ReactNode } from "react";
export function RouterWrapper({ children }: { children: ReactNode }) {
return <RouterProvider>{children}</RouterProvider>;
}
// app/layout.tsx
import { RouterWrapper } from "@/components/providers/RouterWrapper";
export default function RootLayout({ children }) {
return (
<html>
<body>
<RouterWrapper>
{children}
</RouterWrapper>
</body>
</html>
);
}
For standard React apps (Create React App, Vite, etc.):
// App.tsx
import { RouterProvider } from "@page-speed/router";
function App() {
return (
<RouterProvider>
{/* your app */}
</RouterProvider>
);
}
Install the router package:
pnpm add @page-speed/router
For optimal bundle sizes, import components individually:
// Import specific components
import { Container } from "@opensite/ui/components/container";
import { Section } from "@opensite/ui/components/section";
// Or import multiple from grouped export
import { Container, Section, Button } from "@opensite/ui/components";
// Import all (larger bundle)
import * as UI from "@opensite/ui";
Layout container for consistent content width and centering.
import { Container } from "@opensite/ui/components/container";
<Container maxWidth="xl">
<h1>Page Content</h1>
</Container>
Props:
maxWidth?: "sm" | "md" | "lg" | "xl" | "2xl" | "4xl" | "full" - Maximum width (default: "xl")as?: keyof JSX.IntrinsicElements - HTML element to render (default: "div")className?: string - Additional CSS classesSection wrapper with optional title, subtitle, and background variants.
import { Section } from "@opensite/ui/components/section";
<Section
id="features"
title="Our Features"
subtitle="What we offer"
background="gradient"
spacing="lg"
>
<p>Section content here</p>
</Section>
Props:
id?: string - Section ID for anchor linkstitle?: string - Section title (renders as h2)subtitle?: string - Section subtitle/eyebrowbackground?: "white" | "gray" | "dark" | "gradient" | "primary" | "secondary" | "muted" (default: "white")spacing?: "sm" | "md" | "lg" | "xl" (default: "lg")className?: string - Additional CSS classesAnimated modal dialog component using framer-motion with polished default styles.
import { AnimatedDialog } from "@opensite/ui/components/animated-dialog";
import { useState } from "react";
function MyComponent() {
const [open, setOpen] = useState(false);
return (
<AnimatedDialog
open={open}
onOpenChange={setOpen}
title="Welcome"
eyebrow="Hello"
description="This is a modal dialog"
size="lg"
footer={
<button onClick={() => setOpen(false)}>Close</button>
}
>
<p>Dialog content here</p>
</AnimatedDialog>
);
}
Props:
open: boolean - Whether the dialog is open (required)onOpenChange: (open: boolean) => void - Callback when dialog state changes (required)title?: string - Dialog titleeyebrow?: string - Eyebrow text above titledescription?: string - Dialog descriptionheader?: ReactNode - Custom header (overrides title/eyebrow/description)footer?: ReactNode - Footer contentsize?: "sm" | "md" | "lg" | "xl" | "full" (default: "lg")className?: string - Additional CSS classes for containercontentClassName?: string - Additional CSS classes for content areaDefault Styles:
Hero banner component with image or video background support.
import { PageHeroBanner } from "@opensite/ui/components/page-hero-banner";
<PageHeroBanner
imageUrl="https://example.com/hero.jpg"
alt="Hero banner"
minHeight="600px"
showOverlay={true}
overlayOpacity={0.6}
contentMaxWidth="4xl"
>
<h1>Welcome to Our Site</h1>
<p>Discover amazing content</p>
</PageHeroBanner>
Props:
imageUrl?: string - Image URL or Media ID (either imageUrl or videoUrl required)videoUrl?: string - Video URL or Media ID (either imageUrl or videoUrl required)alt?: string - Alt text for image (default: "Hero banner")loading?: "eager" | "lazy" (default: "eager")minHeight?: string (default: "500px")showOverlay?: boolean (default: true)overlayOpacity?: number (default: 0.6)contentMaxWidth?: ContainerMaxWidth (default: "4xl")className?: string - Additional CSS classesInteractive button component with multiple variants and sizes.
import { Button } from "@opensite/ui/components/button";
<Button variant="default" size="md">
Click Me
</Button>
Props:
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" (default: "default")size?: "default" | "sm" | "lg" | "icon" | "icon-sm" | "icon-lg" (default: "default")asChild?: boolean - Render as child component using Radix Slot (default: false)Additional components from shadcn/ui are available:
import { Card, CardHeader, CardContent, CardFooter } from "@opensite/ui/components/card";
import { Badge } from "@opensite/ui/components/badge";
import { Popover, PopoverTrigger, PopoverContent } from "@opensite/ui/components/popover";
Pre-configured, reusable UI blocks for common content patterns.
Display content sections with alternating left/right media placement. Uses the Section component for consistent spacing, backgrounds, and optional titles. Located in the about category.
import { AlternatingBlocks } from "@opensite/ui/blocks/about/alternating-blocks";
<AlternatingBlocks
title="Our Journey"
subtitle="About Us"
background="gray"
spacing="xl"
sections={[
{
content: (
<div>
<h3 className="mb-3 text-2xl font-semibold">Our Story</h3>
<p className="text-muted-foreground">Started in 2018...</p>
</div>
),
media: <img src="story.jpg" alt="Our story" />,
mediaLeft: false
},
{
content: <div>...</div>,
media: <img src="mission.jpg" alt="Our mission" />,
mediaLeft: true
}
]}
/>
Props:
sections: AlternatingBlockSection[] - Array of content sections (required)
content: ReactNode - Content to display (text, headings, etc.)media: ReactNode - Media to display (image, video, icon, etc.)mediaLeft?: boolean - Place media on left side (default: false)title?: string - Section title (optional)subtitle?: string - Section subtitle/eyebrow (optional)background?: SectionBackground - Background variant ("white" | "gray" | "accent", default: "white")spacing?: SectionSpacing - Vertical spacing ("none" | "sm" | "md" | "lg" | "xl", default: "lg")className?: string - Additional CSS classes for Section wrappercontentClassName?: string - Additional CSS classes for content containerNote: Blocks are now organized by category. Import path includes category: @opensite/ui/blocks/[category]/[block-name]
Two-column CTA grid that reveals background imagery or color on hover. Located in the cta category.
import { MediaHoverCtas } from "@opensite/ui/blocks/cta/media-hover-ctas";
<MediaHoverCtas
items={[
{
content: (
<div>
<h3 className="mb-3 text-xl font-semibold">Our Mission</h3>
<p className="text-muted-foreground">Deliver remarkable experiences.</p>
</div>
),
onHoverImgSrc: "/images/mission.jpg",
altText: "Our Mission"
},
{
content: (
<div>
<h3 className="mb-3 text-xl font-semibold">Our Vision</h3>
<p className="text-muted-foreground">Build the future of our industry.</p>
</div>
),
initialBackgroundColor: "var(--brand-100)",
onHoverBackgroundColor: "var(--brand-900)"
}
]}
/>
Props:
items?: MediaHoverCtaItem[] - Array of CTA items (default: [])
content?: ReactNode - Content to render inside the cardonHoverImgSrc?: string - Image URL to reveal on hoverimgHoverClassName?: string - Additional classes for hover imagealtText?: string - Alt text for hover image (leave empty for decorative)cardHref?: string - Optional href to make the card a linkinitialBackgroundColor?: string - CSS color value or variable for base backgroundonHoverBackgroundColor?: string - CSS color value or variable for hover background (ignored when hover image is used)sectionClassName?: string - Additional classes for section wrappergridClassName?: string - Additional classes for grid containeroptixFlowConfig?: { apiKey: string; compression?: number } - Optional Optix Flow config for @page-speed/imgSemantic registry for AI-driven component selection. Maps semantic concepts to available UI blocks.
import {
BLOCK_REGISTRY,
getBlocksBySemanticTag,
getBlocksByCategory,
searchBlocks
} from "@opensite/ui/registry";
// Get blocks by semantic tag
const aboutBlocks = getBlocksBySemanticTag("about");
// Get blocks by category
const featureBlocks = getBlocksByCategory("features");
// Search blocks
const results = searchBlocks("alternating");
Available Functions:
getBlocksBySemanticTag(tag: string) - Find blocks matching semantic taggetBlocksByCategory(category: BlockCategory) - Find blocks in categorygetBlockById(id: string) - Get specific block by IDgetAllBlocks() - Get all registered blocksgetAllCategories() - Get all available categoriessearchBlocks(query: string) - Search blocks by name/description/tagsbuilder-contract-bundle.json is the versioned machine-readable contract for the semantic builder pipeline. It is generated from the same registry/export path as registry-export.json, but adds the structural rules that downstream services enforce:
blocks expose stable blockRef values derived from public @opensite/ui/blocks/* exports.sharedLayout declares the canonical _layout.header and _layout.footer sources.dynamicSources keeps symbolic sources such as blog_feed in canonical page JSON until dashtrack-ai hydrates them at routing-build time.designTokens treats theme_config as canonical and tailwind_css as derived.pageRules documents the route-centric payload shape (block_name, block_ref, data) that rendering must follow exactly.If you change a block's public export path, ID, category, semantic tags, or registry description, rebuild the package so the contract bundle stays authoritative.
Block Categories:
For comprehensive styling documentation including all CSS variables, theming guides, and customization examples, see STYLES.md.
Components use CSS variables for theming. Define these in your global CSS:
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
/* ... more variables */
}
Ensure your tailwind.config.js includes the library components:
module.exports = {
content: [
"./src/**/*.{js,ts,jsx,tsx}",
"./node_modules/@opensite/ui/dist/**/*.{js,mjs}",
],
theme: {
extend: {
// Your custom theme
},
},
};
Override component styles using the className prop:
<Container className="bg-blue-500 text-white px-8">
Custom styled container
</Container>
Full TypeScript support with exported types:
import type {
ContainerProps,
ContainerMaxWidth,
SectionProps,
SectionBackground,
SectionSpacing,
AnimatedDialogProps,
PageHeroBannerProps,
AlternatingBlocksProps,
AlternatingBlockSection,
BlockRegistryEntry,
BlockCategory,
} from "@opensite/ui/types";
All components are optimized for:
The library is fully tree-shakable. Import only what you need:
// ✅ Good - Only imports Container (~1KB)
import { Container } from "@opensite/ui/components/container";
// ❌ Avoid - Imports everything (~50KB)
import * as UI from "@opensite/ui";
pnpm build
pnpm build also regenerates:
package.json export maps (via generate:exports)registry-export.json (via scripts/export-registry.js)builder-contract-bundle.json (via scripts/export-registry.js)If you change a block's design/intent, update its registry metadata in
src/registry/blocks.ts before building so the exported JSON and builder contract stay accurate.
# Run tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Run tests with coverage
pnpm test -- --coverage
pnpm type-check
BSD 3-Clause
FAQs
Foundational UI component library for OpenSite Semantic Site Builder with tree-shakable exports and abstract styling
The npm package @opensite/ui receives a total of 1,266 weekly downloads. As such, @opensite/ui popularity was classified as popular.
We found that @opensite/ui demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Socket found 37 malicious PyPI wheels that abuse Python startup hooks to launch a Bun-powered credential stealer tied to Mini Shai-Hulud/Miasma.

Security News
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.