
Security News
November CVEs Fell 25% YoY, Driven by Slowdowns at Major CNAs
November CVE publications fell 25% YoY even as 2025 totals rose, showing how a few major CNAs can swing “global” counts and skew perceived risk.
mosaic-grid-package
Advanced tools
A framework-agnostic web component for creating beautiful, animated mosaic-style content grids with lazy loading and custom previews
A framework-agnostic web component for creating beautiful, animated mosaic-style content grids. Display images, PDFs, videos, markdown files, and external links in a responsive grid layout with smooth expand/collapse animations.
Inspiration: This package was originally inspired by the beautiful mosaic grid design from CodePen by iamsaief. We've reimagined it as a reusable, type-safe web component with enhanced features including lazy loading, custom previews, and support for multiple content types.
requestAnimationFrame and GPU acceleration for smooth interactionsnpm install mosaic-grid-package
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Mosaic Grid Demo</title>
</head>
<body>
<mosaic-grid-widget></mosaic-grid-widget>
<script type="module">
import 'mosaic-grid-package';
import { MosaicItem } from 'mosaic-grid-package/types';
const items: MosaicItem[] = [
{
id: '1',
type: 'image',
preview: 'https://example.com/thumb.jpg',
full: 'https://example.com/full.jpg',
layout: 'normal',
title: 'My Image'
},
{
id: '2',
type: 'pdf',
preview: 'https://example.com/pdf-thumb.png',
src: 'https://example.com/document.pdf',
layout: 'tall',
title: 'My PDF'
}
];
document.addEventListener('DOMContentLoaded', () => {
const grid = document.querySelector('mosaic-grid-widget');
if (grid) {
grid.items = items;
}
});
</script>
</body>
</html>
The package is built as a Web Component using the Custom Elements API and Shadow DOM:
<mosaic-grid-widget> is registered as a custom HTML elementitems property populates the grid with tilesThe component uses CSS Grid with auto-fit and minmax for responsive behavior:
The grid automatically adjusts based on screen size:
The component handles different content types through a discriminated union pattern:
Images are automatically lazy-loaded using the Intersection Observer API. Images start loading when they're within 200px of the viewport, improving initial page load performance. Custom HTML previews (previewHtml or previewRenderer) are rendered immediately and bypass lazy loading.
<mosaic-grid-widget></mosaic-grid-widget>
items (setter)Sets the grid items. Accepts an array of MosaicItem objects.
grid.items = [
{ id: '1', type: 'image', preview: '...', full: '...' },
// ... more items
];
MosaicItemThe base type for all grid items. Uses discriminated unions based on the type field.
type MosaicItem = ImageItem | PdfItem | MarkdownItem | VideoItem | LinkItem | CustomItem
ImageItem{
id: string;
type: 'image';
preview: string; // URL for thumbnail/background (lazy-loaded)
full: string; // URL for full-resolution image
title?: string; // Optional title
layout?: LayoutType; // 'normal' | 'wide' | 'tall' | 'big'
previewHtml?: string; // Optional custom HTML for preview
previewRenderer?: PreviewRenderHandler; // Optional function to generate preview HTML
}
PdfItem{
id: string;
type: 'pdf';
preview: string; // URL for PDF thumbnail
src: string; // URL to PDF file
title?: string;
layout?: LayoutType;
}
MarkdownItem{
id: string;
type: 'markdown';
preview: string; // URL for markdown thumbnail
src: string; // URL to markdown file
title?: string;
layout?: LayoutType;
}
VideoItem{
id: string;
type: 'video';
preview: string; // URL for video thumbnail
src: string; // URL to video file
title?: string;
layout?: LayoutType;
}
LinkItem{
id: string;
type: 'external_link';
preview: string; // URL for link thumbnail
url: string; // URL to open
title?: string;
layout?: LayoutType;
}
CustomItem{
id: string;
type: 'custom';
preview: string; // Fallback preview URL (for accessibility)
handler: CustomRenderHandler; // Function that returns HTML when tile is clicked
previewHtml?: string; // Optional custom HTML for preview
previewRenderer?: PreviewRenderHandler; // Optional function to generate preview HTML
title?: string;
layout?: LayoutType;
}
LayoutTypetype LayoutType = 'normal' | 'wide' | 'tall' | 'big';
PreviewRenderHandlertype PreviewRenderHandler = (item: MosaicItem) => string;
Synchronous function that returns HTML string for tile preview. Used for custom previews that don't require async operations.
CustomRenderHandlertype CustomRenderHandler = (item: MosaicItem) => Promise<string>;
Async function that returns HTML string when a custom tile is clicked. Used for dynamic content loading.
const imageGallery: MosaicItem[] = [
{
id: 'img1',
type: 'image',
preview: '/thumbnails/photo1-thumb.jpg',
full: '/photos/photo1-full.jpg',
layout: 'big',
title: 'Sunset over mountains'
},
{
id: 'img2',
type: 'image',
preview: '/thumbnails/photo2-thumb.jpg',
full: '/photos/image2-full.jpg',
layout: 'wide',
title: 'Ocean waves'
}
];
const customTile: MosaicItem = {
id: 'custom1',
type: 'image',
preview: 'data:image/svg+xml,<svg>...</svg>', // Fallback
full: 'https://example.com/image.jpg',
previewHtml: `
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;">
<svg width="64" height="64"><circle cx="32" cy="32" r="24" fill="white"/></svg>
</div>
`,
layout: 'normal'
};
const customRendererTile: MosaicItem = {
id: 'custom2',
type: 'image',
preview: 'fallback.jpg',
full: 'https://example.com/image.jpg',
previewRenderer: (item) => {
// Generate custom HTML based on item properties
return `<div style="background: radial-gradient(circle, ${item.id === 'custom2' ? '#ff6b6b' : '#4ecdc4'});
width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;">
<span style="color: white; font-size: 24px;">Custom</span>
</div>`;
},
layout: 'normal'
};
const customContentTile: CustomItem = {
id: 'custom3',
type: 'custom',
preview: 'placeholder.jpg',
handler: async (item) => {
// Fetch or generate content dynamically
const response = await fetch('https://api.example.com/content');
const data = await response.json();
return `<div class="custom-content">${data.html}</div>`;
},
previewHtml: '<div style="background: #667eea; color: white; padding: 20px;">Click to load</div>',
layout: 'normal'
};
const mixedContent: MosaicItem[] = [
{
id: 'doc1',
type: 'pdf',
preview: '/thumbnails/report-thumb.png',
src: '/documents/annual-report.pdf',
layout: 'tall',
title: 'Annual Report 2024'
},
{
id: 'readme',
type: 'markdown',
preview: '/thumbnails/readme-thumb.png',
src: 'https://raw.githubusercontent.com/user/repo/main/README.md',
layout: 'normal',
title: 'Project README'
},
{
id: 'video1',
type: 'video',
preview: '/thumbnails/video-thumb.jpg',
src: '/videos/demo.mp4',
layout: 'wide',
title: 'Product Demo'
},
{
id: 'link1',
type: 'external_link',
preview: '/thumbnails/external-thumb.png',
url: 'https://example.com',
layout: 'normal',
title: 'Visit Website'
}
];
The component uses Shadow DOM, so styles are encapsulated. However, you can style the component's container:
mosaic-grid-widget {
display: block;
width: 100%;
max-width: 1200px;
margin: 0 auto;
}
The internal grid uses:
npm run build
Builds the package to dist/ directory with both ES module and UMD formats.
npm run dev
Starts Vite dev server with the demo page.
npm test
Runs Vitest test suite with jsdom environment.
mosaic-grids/
??? src/
? ??? mosaic-grid.ts # Main component class
? ??? types.ts # TypeScript type definitions
??? demo/
? ??? index.html # Demo page
? ??? main.ts # Demo implementation
??? tests/
? ??? mosaic-grid.test.ts # Test suite
??? dist/ # Built files
??? package.json
The component uses Shadow DOM with mode: 'open' to:
The component includes several performance optimizations:
translateZ(0) to trigger hardware accelerationcontain: layout style paint for rendering isolationThe component maintains internal state:
_items: Array of grid items_state: Current grid state ('idle' | 'item-expanded' | 'loading')expandedTile: Reference to currently expanded tileAnimations use CSS transitions with cubic-bezier easing:
TypeScript discriminated unions ensure:
type fieldmarked or markdown-it.Contributions are welcome! Please ensure:
npm test)[Add your license here]
FAQs
A framework-agnostic web component for creating beautiful, animated mosaic-style content grids with lazy loading and custom previews
We found that mosaic-grid-package demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer 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.

Security News
November CVE publications fell 25% YoY even as 2025 totals rose, showing how a few major CNAs can swing “global” counts and skew perceived risk.

Security News
React disclosed a CVSS 10.0 RCE in React Server Components and is advising users to upgrade affected packages and frameworks to patched versions now.

Research
/Security News
We spotted a wave of auto-generated “elf-*” npm packages published every two minutes from new accounts, with simple malware variants and early takedowns underway.