
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.
@page-speed/skins
Advanced tools
Lightweight JSON-based skin system for platform-wide component styling. CDN-accessible configuration files for video players, carousels, and more.
Lightweight JSON-based skin system for platform-wide component styling. CDN-accessible configuration files for video players, carousels, and more.
npm install @page-speed/skins
# or
pnpm add @page-speed/skins
# or
yarn add @page-speed/skins
import { loadSkinFromJsDelivr } from '@page-speed/skins';
// Load the base video skin
const skin = await loadSkinFromJsDelivr('0.1.0', 'skins/video/base.json');
console.log(skin.name); // "Base Video Skin"
console.log(skin.tokens); // CSS variables
console.log(skin.classes); // Tailwind classes
import { applySkinToElement } from '@page-speed/skins';
const videoContainer = document.getElementById('video-container');
applySkinToElement(videoContainer, skin);
// Now the element has:
// - CSS variables set via style.setProperty
// - data-skin-id, data-skin-version attributes
import { Video } from '@page-speed/video';
import { getVideoSkinAttributes } from '@page-speed/skins';
const skin = await loadSkinFromJsDelivr('0.1.0', 'skins/video/linear-inspired.json');
const attrs = getVideoSkinAttributes(skin);
<Video
src="video.mp4"
{...attrs}
/>
| Skin | File | Description |
|---|---|---|
| Base | skins/video/base.json | Minimal base skin with essential styling |
| Linear-Inspired | skins/video/linear-inspired.json | Clean, modern design inspired by Linear |
| Minimal Hover | skins/video/minimal-hover.json | Controls appear on hover |
| Social Card | skins/video/social-card.json | Optimized for social media embeds |
| YouTube Classic | skins/video/youtube-classic.json | Familiar YouTube-style design |
https://cdn.jsdelivr.net/npm/@page-speed/skins@0.1.0/skins/video/base.json
https://cdn.jsdelivr.net/npm/@page-speed/skins@0.1.0/skins/video/linear-inspired.json
https://cdn.jsdelivr.net/npm/@page-speed/skins@0.1.0/skins/video/minimal-hover.json
https://cdn.jsdelivr.net/npm/@page-speed/skins@0.1.0/skins/video/social-card.json
https://cdn.jsdelivr.net/npm/@page-speed/skins@0.1.0/skins/video/youtube-classic.json
interface SkinDefinition {
id: string; // Unique identifier
name: string; // Human-readable name
version: string; // Semantic version
targets: string[]; // Component types: ["video"]
tokens: SkinTokens; // CSS variables
classes: SkinClasses; // Tailwind/CSS classes
assets?: SkinAssets; // Optional icons/images
description?: string; // Optional description
metadata?: object; // Optional metadata
}
Applies a complete skin to an HTML element.
const container = document.getElementById('my-container');
applySkinToElement(container, skin);
Gets classes for a specific slot.
const playButtonClasses = resolveClasses(skin, 'playButton');
// "flex items-center justify-center w-10 h-10 ..."
Gets CSS variables as a style object (useful for React).
const styleObj = getSkinStyleObject(skin);
// { "--video-bg": "#000", "--video-accent-color": "#3b82f6", ... }
<div style={styleObj}>...</div>
Gets all attributes for a <video> element.
const attrs = getVideoSkinAttributes(skin);
// {
// className: "w-full h-full object-contain",
// style: { "--video-bg": "#000", ... },
// "data-skin-id": "video-base",
// ...
// }
<video {...attrs} src="video.mp4" />
Gets all video-specific classes.
const classes = resolveVideoClasses(skin);
// {
// container: "relative w-full ...",
// video: "w-full h-full ...",
// controlsBar: "absolute bottom-0 ...",
// playButton: "flex items-center ...",
// ...
// }
Loads a skin from any URL with caching.
const skin = await loadSkin({
url: 'https://example.com/skin.json',
cache: true,
timeout: 10000,
debug: false,
});
Convenience wrapper for jsDelivr CDN.
const skin = await loadSkinFromJsDelivr(
'0.1.0',
'skins/video/base.json'
);
Preloads multiple skins in parallel.
await preloadSkins([
'https://cdn.jsdelivr.net/npm/@page-speed/skins@0.1.0/skins/video/base.json',
'https://cdn.jsdelivr.net/npm/@page-speed/skins@0.1.0/skins/video/linear-inspired.json',
]);
fetch('https://cdn.jsdelivr.net/npm/@page-speed/skins@0.1.0/skins/video/base.json')
.then(r => r.json())
.then(skin => {
const video = document.querySelector('video');
Object.entries(skin.tokens).forEach(([key, value]) => {
video.style.setProperty(`--${key}`, value);
});
});
import { useEffect, useState } from 'react';
import { loadSkinFromJsDelivr, getVideoSkinAttributes } from '@page-speed/skins';
import { Video } from '@page-speed/video';
function VideoWithSkin() {
const [skin, setSkin] = useState(null);
useEffect(() => {
loadSkinFromJsDelivr('0.1.0', 'skins/video/linear-inspired.json')
.then(setSkin);
}, []);
if (!skin) return <div>Loading skin...</div>;
const attrs = getVideoSkinAttributes(skin);
return (
<Video
src="video.mp4"
{...attrs}
/>
);
}
import { resolveVideoClasses, getSkinStyleObject } from '@page-speed/skins';
function VideoPlayer({ skin, src }) {
const classes = resolveVideoClasses(skin);
const style = getSkinStyleObject(skin);
return (
<div className={classes.container} style={style}>
<video className={classes.video} src={src} />
<div className={classes.controlsBar}>
<button className={classes.playButton}>Play</button>
<div className={classes.timeline}>
<div className={classes.timelineProgress} />
</div>
<span className={classes.timeText}>0:00 / 5:23</span>
</div>
</div>
);
}
const [currentSkin, setCurrentSkin] = useState('base');
const skinUrls = {
base: 'skins/video/base.json',
linear: 'skins/video/linear-inspired.json',
minimal: 'skins/video/minimal-hover.json',
};
const skin = await loadSkinFromJsDelivr('0.1.0', skinUrls[currentSkin]);
{
"id": "my-custom-skin",
"name": "My Custom Skin",
"version": "1.0.0",
"targets": ["video"],
"tokens": {
"--video-bg": "#000000",
"--video-accent-color": "#3b82f6"
},
"classes": {
"container": "relative w-full bg-black",
"video": "w-full h-full",
"playButton": "flex items-center justify-center w-10 h-10"
},
"assets": {
"playIcon": "data:image/svg+xml,..."
}
}
Standard slots for video players:
container - Wrapper elementvideo - Video element itselfcontrolsBar - Controls containerplayButton - Play/pause buttontimeline - Progress bartimelineProgress - Progress indicatortimelineBuffered - Buffered indicatortimeText - Time displayvolumeControl - Volume controlfullscreenButton - Fullscreen buttonsettingsButton - Settings buttonloadingSpinner - Loading indicatorplayOverlay - Large play button overlayhttps://cdn.jsdelivr.net/npm/@page-speed/skins@{version}/{path}
Examples:
https://cdn.jsdelivr.net/npm/@page-speed/skins@0.1.0/skins/video/base.json
https://cdn.jsdelivr.net/npm/@page-speed/skins@latest/skins/video/linear-inspired.json
https://unpkg.com/@page-speed/skins@{version}/{path}
import baseSkin from '@page-speed/skins/skins/video/base.json';
Full TypeScript support with strict types:
import type { SkinDefinition, VideoSkinSlots } from '@page-speed/skins';
const skin: SkinDefinition = {
id: 'my-skin',
name: 'My Skin',
version: '1.0.0',
targets: ['video'],
tokens: {},
classes: {},
};
The system is designed to support any component type:
{
"targets": ["carousel"],
"classes": {
"container": "...",
"slide": "...",
"navButton": "...",
"indicator": "..."
}
}
skins/{component-type}/{skin-name}.jsonpnpm run validate:skinspackage.json exports mapBSD-3-Clause
OpenSite AI (https://opensite.ai)
FAQs
Lightweight JSON-based skin system for platform-wide component styling. CDN-accessible configuration files for video players, carousels, and more.
We found that @page-speed/skins 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.